govuk_publishing_components 32.1.0 → 35.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (496) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/assets/config/govuk_publishing_components_manifest.js +75 -1
  4. data/app/assets/images/govuk_publishing_components/icon-search.svg +3 -0
  5. data/app/assets/images/govuk_publishing_components/youtube-play-icon.png +0 -0
  6. data/app/assets/images/govuk_publishing_components/youtube-play-icon.svg +4 -0
  7. data/app/assets/javascripts/component_guide/accessibility-test.js +0 -1
  8. data/app/assets/javascripts/govuk_publishing_components/analytics/auto-scroll-tracker.js +2 -1
  9. data/app/assets/javascripts/govuk_publishing_components/analytics/linked-domains.js +135 -0
  10. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-auto-tracker.js +49 -0
  11. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +341 -27
  12. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-ecommerce-tracker.js +17 -92
  13. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js +11 -23
  14. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-form-tracker.js +121 -0
  15. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +86 -305
  16. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-page-views.js +13 -6
  17. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-schemas.js +31 -1
  18. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-smart-answer-results-tracker.js +57 -0
  19. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js +131 -0
  20. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/init-ga4.js +9 -4
  21. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/pii-remover.js +4 -2
  22. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +4 -0
  23. data/app/assets/javascripts/govuk_publishing_components/components/accordion.js +12 -1
  24. data/app/assets/javascripts/govuk_publishing_components/components/cookie-banner.js +2 -2
  25. data/app/assets/javascripts/govuk_publishing_components/components/image-card.js +19 -0
  26. data/app/assets/javascripts/govuk_publishing_components/components/layout-super-navigation-header.js +2 -81
  27. data/app/assets/javascripts/govuk_publishing_components/components/single-page-notification-button.js +24 -8
  28. data/app/assets/javascripts/govuk_publishing_components/components/step-by-step-nav.js +23 -1
  29. data/app/assets/javascripts/govuk_publishing_components/dependencies.js +13 -0
  30. data/app/assets/javascripts/govuk_publishing_components/lib/govspeak/youtube-link-enhancement.js +3 -2
  31. data/app/assets/javascripts/govuk_publishing_components/lib/trigger-event.js +1 -0
  32. data/app/assets/javascripts/govuk_publishing_components/load-analytics.js +116 -0
  33. data/app/assets/javascripts/govuk_publishing_components/modules.js +7 -2
  34. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +140 -191
  35. data/app/assets/stylesheets/govuk_publishing_components/_individual_component_support.scss +10 -0
  36. data/app/assets/stylesheets/govuk_publishing_components/components/_accordion.scss +29 -0
  37. data/app/assets/stylesheets/govuk_publishing_components/components/_action-link.scss +2 -0
  38. data/app/assets/stylesheets/govuk_publishing_components/components/_attachment.scss +2 -0
  39. data/app/assets/stylesheets/govuk_publishing_components/components/_back-link.scss +1 -0
  40. data/app/assets/stylesheets/govuk_publishing_components/components/_big-number.scss +4 -5
  41. data/app/assets/stylesheets/govuk_publishing_components/components/_breadcrumbs.scss +1 -0
  42. data/app/assets/stylesheets/govuk_publishing_components/components/_button.scss +12 -0
  43. data/app/assets/stylesheets/govuk_publishing_components/components/_cards.scss +20 -0
  44. data/app/assets/stylesheets/govuk_publishing_components/components/_character-count.scss +1 -0
  45. data/app/assets/stylesheets/govuk_publishing_components/components/_checkboxes.scss +1 -0
  46. data/app/assets/stylesheets/govuk_publishing_components/components/_contents-list.scss +21 -0
  47. data/app/assets/stylesheets/govuk_publishing_components/components/_contextual-guidance.scss +2 -0
  48. data/app/assets/stylesheets/govuk_publishing_components/components/_contextual-sidebar.scss +2 -0
  49. data/app/assets/stylesheets/govuk_publishing_components/components/_cookie-banner.scss +7 -0
  50. data/app/assets/stylesheets/govuk_publishing_components/components/_date-input.scss +1 -0
  51. data/app/assets/stylesheets/govuk_publishing_components/components/_details.scss +1 -0
  52. data/app/assets/stylesheets/govuk_publishing_components/components/_devolved-nations.scss +2 -0
  53. data/app/assets/stylesheets/govuk_publishing_components/components/_document-list.scss +8 -0
  54. data/app/assets/stylesheets/govuk_publishing_components/components/_emergency-banner.scss +8 -0
  55. data/app/assets/stylesheets/govuk_publishing_components/components/_error-alert.scss +2 -0
  56. data/app/assets/stylesheets/govuk_publishing_components/components/_error-message.scss +1 -0
  57. data/app/assets/stylesheets/govuk_publishing_components/components/_error-summary.scss +1 -0
  58. data/app/assets/stylesheets/govuk_publishing_components/components/_feedback.scss +17 -19
  59. data/app/assets/stylesheets/govuk_publishing_components/components/_fieldset.scss +1 -0
  60. data/app/assets/stylesheets/govuk_publishing_components/components/_file-upload.scss +1 -0
  61. data/app/assets/stylesheets/govuk_publishing_components/components/_govspeak-html-publication.scss +10 -0
  62. data/app/assets/stylesheets/govuk_publishing_components/components/_govspeak.scss +85 -0
  63. data/app/assets/stylesheets/govuk_publishing_components/components/_heading.scss +2 -0
  64. data/app/assets/stylesheets/govuk_publishing_components/components/_hint.scss +1 -0
  65. data/app/assets/stylesheets/govuk_publishing_components/components/_image-card.scss +85 -6
  66. data/app/assets/stylesheets/govuk_publishing_components/components/_input.scss +6 -6
  67. data/app/assets/stylesheets/govuk_publishing_components/components/_inset-text.scss +1 -0
  68. data/app/assets/stylesheets/govuk_publishing_components/components/_intervention.scss +2 -0
  69. data/app/assets/stylesheets/govuk_publishing_components/components/_inverse-header.scss +2 -0
  70. data/app/assets/stylesheets/govuk_publishing_components/components/_label.scss +1 -0
  71. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-footer.scss +1 -0
  72. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-for-public.scss +2 -0
  73. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +253 -483
  74. data/app/assets/stylesheets/govuk_publishing_components/components/_lead-paragraph.scss +2 -0
  75. data/app/assets/stylesheets/govuk_publishing_components/components/_metadata.scss +2 -0
  76. data/app/assets/stylesheets/govuk_publishing_components/components/_modal-dialogue.scss +2 -0
  77. data/app/assets/stylesheets/govuk_publishing_components/components/_notice.scss +1 -0
  78. data/app/assets/stylesheets/govuk_publishing_components/components/_organisation-logo.scss +2 -0
  79. data/app/assets/stylesheets/govuk_publishing_components/components/_panel.scss +1 -0
  80. data/app/assets/stylesheets/govuk_publishing_components/components/_phase-banner.scss +1 -0
  81. data/app/assets/stylesheets/govuk_publishing_components/components/_previous-and-next-navigation.scss +1 -0
  82. data/app/assets/stylesheets/govuk_publishing_components/components/_print-link.scss +2 -0
  83. data/app/assets/stylesheets/govuk_publishing_components/components/_radio.scss +1 -0
  84. data/app/assets/stylesheets/govuk_publishing_components/components/_related-navigation.scss +2 -0
  85. data/app/assets/stylesheets/govuk_publishing_components/components/_reorderable-list.scss +2 -0
  86. data/app/assets/stylesheets/govuk_publishing_components/components/_search.scss +2 -7
  87. data/app/assets/stylesheets/govuk_publishing_components/components/_select.scss +1 -0
  88. data/app/assets/stylesheets/govuk_publishing_components/components/_share-links.scss +2 -0
  89. data/app/assets/stylesheets/govuk_publishing_components/components/_show-password.scss +2 -0
  90. data/app/assets/stylesheets/govuk_publishing_components/components/_signup-link.scss +2 -0
  91. data/app/assets/stylesheets/govuk_publishing_components/components/_single-page-notification-button.scss +2 -0
  92. data/app/assets/stylesheets/govuk_publishing_components/components/_skip-link.scss +1 -0
  93. data/app/assets/stylesheets/govuk_publishing_components/components/_step-by-step-nav-header.scss +17 -0
  94. data/app/assets/stylesheets/govuk_publishing_components/components/_step-by-step-nav-related.scss +2 -0
  95. data/app/assets/stylesheets/govuk_publishing_components/components/_step-by-step-nav.scss +8 -0
  96. data/app/assets/stylesheets/govuk_publishing_components/components/_subscription-links.scss +2 -1
  97. data/app/assets/stylesheets/govuk_publishing_components/components/_success-alert.scss +1 -0
  98. data/app/assets/stylesheets/govuk_publishing_components/components/_summary-list.scss +1 -0
  99. data/app/assets/stylesheets/govuk_publishing_components/components/_table.scss +1 -0
  100. data/app/assets/stylesheets/govuk_publishing_components/components/_tabs.scss +1 -0
  101. data/app/assets/stylesheets/govuk_publishing_components/components/_textarea.scss +17 -0
  102. data/app/assets/stylesheets/govuk_publishing_components/components/_title.scss +12 -0
  103. data/app/assets/stylesheets/govuk_publishing_components/components/_translation-nav.scss +2 -0
  104. data/app/assets/stylesheets/govuk_publishing_components/components/_warning-text.scss +1 -0
  105. data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_steps.scss +26 -9
  106. data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_tables.scss +12 -0
  107. data/app/assets/stylesheets/govuk_publishing_components/components/helpers/_brand-colours.scss +2 -2
  108. data/app/assets/stylesheets/govuk_publishing_components/components/helpers/_contents-list-helper.scss +0 -1
  109. data/app/controllers/govuk_publishing_components/application_controller.rb +0 -13
  110. data/app/controllers/govuk_publishing_components/audit_controller.rb +1 -1
  111. data/app/controllers/govuk_publishing_components/component_guide_controller.rb +6 -9
  112. data/app/models/govuk_publishing_components/audit_applications.rb +10 -19
  113. data/app/models/govuk_publishing_components/audit_comparer.rb +29 -24
  114. data/app/models/govuk_publishing_components/audit_components.rb +157 -82
  115. data/app/models/govuk_publishing_components/component_doc.rb +14 -0
  116. data/app/models/govuk_publishing_components/component_wrapper_helper_options.rb +15 -0
  117. data/app/views/govuk_publishing_components/audit/_applications.html.erb +3 -3
  118. data/app/views/govuk_publishing_components/audit/_component_contents.html.erb +47 -39
  119. data/app/views/govuk_publishing_components/audit/_components.html.erb +47 -2
  120. data/app/views/govuk_publishing_components/component_guide/_application_stylesheet.html.erb +0 -3
  121. data/app/views/govuk_publishing_components/component_guide/index.html.erb +0 -7
  122. data/app/views/govuk_publishing_components/component_guide/show.html.erb +21 -6
  123. data/app/views/govuk_publishing_components/components/_accordion.html.erb +30 -36
  124. data/app/views/govuk_publishing_components/components/_action_link.html.erb +7 -2
  125. data/app/views/govuk_publishing_components/components/_attachment.html.erb +2 -0
  126. data/app/views/govuk_publishing_components/components/_attachment_link.html.erb +2 -0
  127. data/app/views/govuk_publishing_components/components/_back_link.html.erb +2 -0
  128. data/app/views/govuk_publishing_components/components/_big_number.html.erb +3 -1
  129. data/app/views/govuk_publishing_components/components/_breadcrumbs.html.erb +8 -2
  130. data/app/views/govuk_publishing_components/components/_button.html.erb +2 -0
  131. data/app/views/govuk_publishing_components/components/_cards.html.erb +6 -4
  132. data/app/views/govuk_publishing_components/components/_character_count.html.erb +2 -0
  133. data/app/views/govuk_publishing_components/components/_checkboxes.html.erb +6 -2
  134. data/app/views/govuk_publishing_components/components/_contents_list.html.erb +11 -15
  135. data/app/views/govuk_publishing_components/components/_contextual_breadcrumbs.html.erb +7 -4
  136. data/app/views/govuk_publishing_components/components/_contextual_footer.html.erb +2 -0
  137. data/app/views/govuk_publishing_components/components/_contextual_guidance.html.erb +2 -0
  138. data/app/views/govuk_publishing_components/components/_contextual_sidebar.html.erb +13 -6
  139. data/app/views/govuk_publishing_components/components/_cookie_banner.html.erb +12 -6
  140. data/app/views/govuk_publishing_components/components/_date_input.html.erb +2 -0
  141. data/app/views/govuk_publishing_components/components/_details.html.erb +5 -1
  142. data/app/views/govuk_publishing_components/components/_devolved_nations.html.erb +2 -0
  143. data/app/views/govuk_publishing_components/components/_document_list.html.erb +3 -0
  144. data/app/views/govuk_publishing_components/components/_emergency_banner.html.erb +2 -0
  145. data/app/views/govuk_publishing_components/components/_error_alert.html.erb +2 -0
  146. data/app/views/govuk_publishing_components/components/_error_message.html.erb +4 -1
  147. data/app/views/govuk_publishing_components/components/_error_summary.html.erb +29 -26
  148. data/app/views/govuk_publishing_components/components/_feedback.html.erb +2 -0
  149. data/app/views/govuk_publishing_components/components/_fieldset.html.erb +2 -0
  150. data/app/views/govuk_publishing_components/components/_file_upload.html.erb +2 -0
  151. data/app/views/govuk_publishing_components/components/_google_tag_manager_script.html.erb +2 -2
  152. data/app/views/govuk_publishing_components/components/_govspeak.html.erb +1 -0
  153. data/app/views/govuk_publishing_components/components/_govspeak_html_publication.html.erb +2 -0
  154. data/app/views/govuk_publishing_components/components/_heading.html.erb +2 -0
  155. data/app/views/govuk_publishing_components/components/_hint.html.erb +4 -1
  156. data/app/views/govuk_publishing_components/components/_image_card.html.erb +17 -12
  157. data/app/views/govuk_publishing_components/components/_input.html.erb +10 -2
  158. data/app/views/govuk_publishing_components/components/_inset_text.html.erb +2 -0
  159. data/app/views/govuk_publishing_components/components/_intervention.html.erb +2 -0
  160. data/app/views/govuk_publishing_components/components/_inverse_header.html.erb +2 -0
  161. data/app/views/govuk_publishing_components/components/_label.html.erb +7 -3
  162. data/app/views/govuk_publishing_components/components/_layout_footer.html.erb +2 -0
  163. data/app/views/govuk_publishing_components/components/_layout_for_admin.html.erb +8 -5
  164. data/app/views/govuk_publishing_components/components/_layout_for_public.html.erb +8 -3
  165. data/app/views/govuk_publishing_components/components/_layout_header.html.erb +2 -0
  166. data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +238 -267
  167. data/app/views/govuk_publishing_components/components/_lead_paragraph.html.erb +2 -0
  168. data/app/views/govuk_publishing_components/components/_metadata.html.erb +2 -0
  169. data/app/views/govuk_publishing_components/components/_modal_dialogue.html.erb +2 -0
  170. data/app/views/govuk_publishing_components/components/_notice.html.erb +2 -0
  171. data/app/views/govuk_publishing_components/components/_organisation_logo.html.erb +2 -0
  172. data/app/views/govuk_publishing_components/components/_panel.html.erb +2 -0
  173. data/app/views/govuk_publishing_components/components/_phase_banner.html.erb +3 -1
  174. data/app/views/govuk_publishing_components/components/_previous_and_next_navigation.html.erb +6 -1
  175. data/app/views/govuk_publishing_components/components/_print_link.html.erb +2 -0
  176. data/app/views/govuk_publishing_components/components/_radio.html.erb +2 -0
  177. data/app/views/govuk_publishing_components/components/_related_navigation.html.erb +16 -6
  178. data/app/views/govuk_publishing_components/components/_reorderable_list.html.erb +2 -0
  179. data/app/views/govuk_publishing_components/components/_search.html.erb +2 -0
  180. data/app/views/govuk_publishing_components/components/_select.html.erb +2 -0
  181. data/app/views/govuk_publishing_components/components/_share_links.html.erb +13 -13
  182. data/app/views/govuk_publishing_components/components/_show_password.html.erb +2 -0
  183. data/app/views/govuk_publishing_components/components/_signup_link.html.erb +2 -0
  184. data/app/views/govuk_publishing_components/components/_single_page_notification_button.html.erb +8 -2
  185. data/app/views/govuk_publishing_components/components/_skip_link.html.erb +2 -0
  186. data/app/views/govuk_publishing_components/components/_step_by_step_nav.html.erb +8 -1
  187. data/app/views/govuk_publishing_components/components/_step_by_step_nav_header.html.erb +12 -2
  188. data/app/views/govuk_publishing_components/components/_step_by_step_nav_related.html.erb +21 -8
  189. data/app/views/govuk_publishing_components/components/_subscription_links.html.erb +3 -1
  190. data/app/views/govuk_publishing_components/components/_success_alert.html.erb +2 -0
  191. data/app/views/govuk_publishing_components/components/_summary_list.html.erb +2 -0
  192. data/app/views/govuk_publishing_components/components/_table.html.erb +3 -1
  193. data/app/views/govuk_publishing_components/components/_tabs.html.erb +22 -2
  194. data/app/views/govuk_publishing_components/components/_textarea.html.erb +12 -2
  195. data/app/views/govuk_publishing_components/components/_title.html.erb +2 -0
  196. data/app/views/govuk_publishing_components/components/_translation_nav.html.erb +2 -0
  197. data/app/views/govuk_publishing_components/components/_warning_text.html.erb +2 -0
  198. data/app/views/govuk_publishing_components/components/contextual_sidebar/_ukraine_cta.html.erb +16 -6
  199. data/app/views/govuk_publishing_components/components/docs/accordion.yml +16 -3
  200. data/app/views/govuk_publishing_components/components/docs/action_link.yml +1 -0
  201. data/app/views/govuk_publishing_components/components/docs/breadcrumbs.yml +2 -1
  202. data/app/views/govuk_publishing_components/components/docs/button.yml +10 -0
  203. data/app/views/govuk_publishing_components/components/docs/cards.yml +1 -0
  204. data/app/views/govuk_publishing_components/components/docs/checkboxes.yml +1 -0
  205. data/app/views/govuk_publishing_components/components/docs/contents_list.yml +1 -12
  206. data/app/views/govuk_publishing_components/components/docs/contextual_breadcrumbs.yml +13 -0
  207. data/app/views/govuk_publishing_components/components/docs/contextual_footer.yml +43 -0
  208. data/app/views/govuk_publishing_components/components/docs/contextual_sidebar.yml +65 -0
  209. data/app/views/govuk_publishing_components/components/docs/details.yml +17 -0
  210. data/app/views/govuk_publishing_components/components/docs/document_list.yml +18 -1
  211. data/app/views/govuk_publishing_components/components/docs/emergency_banner.yml +3 -1
  212. data/app/views/govuk_publishing_components/components/docs/error_message.yml +13 -0
  213. data/app/views/govuk_publishing_components/components/docs/govspeak.yml +22 -1
  214. data/app/views/govuk_publishing_components/components/docs/hint.yml +6 -0
  215. data/app/views/govuk_publishing_components/components/docs/image_card.yml +24 -1
  216. data/app/views/govuk_publishing_components/components/docs/input.yml +32 -0
  217. data/app/views/govuk_publishing_components/components/docs/intervention.yml +2 -1
  218. data/app/views/govuk_publishing_components/components/docs/label.yml +17 -0
  219. data/app/views/govuk_publishing_components/components/docs/layout_header.yml +1 -0
  220. data/app/views/govuk_publishing_components/components/docs/previous_and_next_navigation.yml +2 -0
  221. data/app/views/govuk_publishing_components/components/docs/related_navigation.yml +47 -0
  222. data/app/views/govuk_publishing_components/components/docs/share_links.yml +59 -30
  223. data/app/views/govuk_publishing_components/components/docs/single_page_notification_button.yml +16 -1
  224. data/app/views/govuk_publishing_components/components/docs/step_by_step_nav.yml +54 -0
  225. data/app/views/govuk_publishing_components/components/docs/step_by_step_nav_header.yml +6 -0
  226. data/app/views/govuk_publishing_components/components/docs/step_by_step_nav_related.yml +10 -0
  227. data/app/views/govuk_publishing_components/components/docs/tabs.yml +15 -0
  228. data/app/views/govuk_publishing_components/components/docs/textarea.yml +31 -0
  229. data/app/views/govuk_publishing_components/components/docs/translation_nav.yml +2 -0
  230. data/app/views/govuk_publishing_components/components/feedback/_problem_form.html.erb +3 -3
  231. data/app/views/govuk_publishing_components/components/feedback/_survey_signup_form.html.erb +1 -1
  232. data/app/views/govuk_publishing_components/components/feedback/_yes_no_banner.html.erb +4 -5
  233. data/app/views/govuk_publishing_components/components/metadata/_sentence.html.erb +4 -2
  234. data/app/views/govuk_publishing_components/components/related_navigation/_section.html.erb +36 -16
  235. data/app/views/layouts/govuk_publishing_components/application.html.erb +3 -4
  236. data/config/locales/ar.yml +14 -14
  237. data/config/locales/az.yml +15 -14
  238. data/config/locales/be.yml +14 -14
  239. data/config/locales/bg.yml +14 -14
  240. data/config/locales/bn.yml +14 -14
  241. data/config/locales/cs.yml +14 -14
  242. data/config/locales/cy.yml +14 -14
  243. data/config/locales/da.yml +14 -14
  244. data/config/locales/de.yml +14 -14
  245. data/config/locales/dr.yml +14 -14
  246. data/config/locales/el.yml +14 -14
  247. data/config/locales/en.yml +21 -28
  248. data/config/locales/es-419.yml +14 -14
  249. data/config/locales/es.yml +14 -14
  250. data/config/locales/et.yml +14 -14
  251. data/config/locales/fa.yml +15 -14
  252. data/config/locales/fi.yml +14 -14
  253. data/config/locales/fr.yml +14 -14
  254. data/config/locales/gd.yml +14 -14
  255. data/config/locales/gu.yml +14 -14
  256. data/config/locales/he.yml +14 -14
  257. data/config/locales/hi.yml +14 -14
  258. data/config/locales/hr.yml +14 -14
  259. data/config/locales/hu.yml +14 -14
  260. data/config/locales/hy.yml +14 -14
  261. data/config/locales/id.yml +14 -14
  262. data/config/locales/is.yml +14 -14
  263. data/config/locales/it.yml +14 -14
  264. data/config/locales/ja.yml +14 -14
  265. data/config/locales/ka.yml +14 -14
  266. data/config/locales/kk.yml +14 -14
  267. data/config/locales/ko.yml +14 -14
  268. data/config/locales/lt.yml +14 -14
  269. data/config/locales/lv.yml +14 -14
  270. data/config/locales/ms.yml +14 -14
  271. data/config/locales/mt.yml +14 -14
  272. data/config/locales/nl.yml +14 -14
  273. data/config/locales/no.yml +14 -14
  274. data/config/locales/pa-pk.yml +14 -14
  275. data/config/locales/pa.yml +14 -14
  276. data/config/locales/pl.yml +14 -14
  277. data/config/locales/ps.yml +14 -14
  278. data/config/locales/pt.yml +14 -14
  279. data/config/locales/ro.yml +14 -14
  280. data/config/locales/ru.yml +14 -14
  281. data/config/locales/si.yml +14 -14
  282. data/config/locales/sk.yml +14 -14
  283. data/config/locales/sl.yml +14 -14
  284. data/config/locales/so.yml +14 -14
  285. data/config/locales/sq.yml +14 -14
  286. data/config/locales/sr.yml +14 -14
  287. data/config/locales/sv.yml +14 -14
  288. data/config/locales/sw.yml +14 -14
  289. data/config/locales/ta.yml +14 -14
  290. data/config/locales/th.yml +14 -14
  291. data/config/locales/tk.yml +14 -14
  292. data/config/locales/tr.yml +14 -14
  293. data/config/locales/uk.yml +14 -14
  294. data/config/locales/ur.yml +14 -14
  295. data/config/locales/uz.yml +14 -14
  296. data/config/locales/vi.yml +14 -14
  297. data/config/locales/zh-hk.yml +14 -14
  298. data/config/locales/zh-tw.yml +14 -14
  299. data/config/locales/zh.yml +14 -14
  300. data/lib/govuk_publishing_components/app_helpers/asset_helper.rb +86 -0
  301. data/lib/govuk_publishing_components/config.rb +3 -3
  302. data/lib/govuk_publishing_components/presenters/accordion_helper.rb +38 -0
  303. data/lib/govuk_publishing_components/presenters/breadcrumb_selector.rb +3 -2
  304. data/lib/govuk_publishing_components/presenters/{breadcrumbs.rb → breadcrumbs_helper.rb} +8 -0
  305. data/lib/govuk_publishing_components/presenters/button_helper.rb +9 -2
  306. data/lib/govuk_publishing_components/presenters/component_wrapper_helper.rb +95 -0
  307. data/lib/govuk_publishing_components/presenters/image_card_helper.rb +65 -2
  308. data/lib/govuk_publishing_components/presenters/page_with_step_by_step_navigation.rb +3 -2
  309. data/lib/govuk_publishing_components/presenters/related_navigation_helper.rb +16 -7
  310. data/lib/govuk_publishing_components/presenters/single_page_notification_button_helper.rb +43 -2
  311. data/lib/govuk_publishing_components/presenters/step_by_step_nav_helper.rb +16 -0
  312. data/lib/govuk_publishing_components/railtie.rb +7 -0
  313. data/lib/govuk_publishing_components/version.rb +1 -1
  314. data/lib/govuk_publishing_components.rb +6 -1
  315. data/node_modules/axe-core/README.md +1 -1
  316. data/node_modules/axe-core/axe.d.ts +76 -24
  317. data/node_modules/axe-core/axe.js +9200 -9059
  318. data/node_modules/axe-core/axe.min.js +3 -3
  319. data/node_modules/axe-core/package.json +29 -29
  320. data/node_modules/axe-core/sri-history.json +20 -0
  321. data/node_modules/govuk-frontend/README.md +1 -2
  322. data/node_modules/govuk-frontend/govuk/all.js +4035 -2673
  323. data/node_modules/govuk-frontend/govuk/all.js.map +1 -0
  324. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +71 -0
  325. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js.map +1 -0
  326. data/node_modules/govuk-frontend/govuk/common/index.js +180 -0
  327. data/node_modules/govuk-frontend/govuk/common/index.js.map +1 -0
  328. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +376 -0
  329. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js.map +1 -0
  330. data/node_modules/govuk-frontend/govuk/common.js +173 -30
  331. data/node_modules/govuk-frontend/govuk/common.js.map +1 -0
  332. data/node_modules/govuk-frontend/govuk/components/_all.scss +1 -1
  333. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +23 -4
  334. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +2116 -983
  335. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js.map +1 -0
  336. data/node_modules/govuk-frontend/govuk/components/accordion/fixtures.json +54 -22
  337. data/node_modules/govuk-frontend/govuk/components/accordion/macro-options.json +36 -0
  338. data/node_modules/govuk-frontend/govuk/components/accordion/template.njk +8 -2
  339. data/node_modules/govuk-frontend/govuk/components/back-link/_index.scss +19 -19
  340. data/node_modules/govuk-frontend/govuk/components/back-link/fixtures.json +12 -12
  341. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +21 -10
  342. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/fixtures.json +22 -22
  343. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +23 -5
  344. data/node_modules/govuk-frontend/govuk/components/button/button.js +958 -690
  345. data/node_modules/govuk-frontend/govuk/components/button/button.js.map +1 -0
  346. data/node_modules/govuk-frontend/govuk/components/button/fixtures.json +85 -66
  347. data/node_modules/govuk-frontend/govuk/components/button/template.njk +1 -1
  348. data/node_modules/govuk-frontend/govuk/components/character-count/_index.scss +9 -0
  349. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +2083 -1161
  350. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js.map +1 -0
  351. data/node_modules/govuk-frontend/govuk/components/character-count/fixtures.json +112 -36
  352. data/node_modules/govuk-frontend/govuk/components/character-count/macro-options.json +42 -0
  353. data/node_modules/govuk-frontend/govuk/components/character-count/template.njk +27 -3
  354. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +1155 -1119
  355. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js.map +1 -0
  356. data/node_modules/govuk-frontend/govuk/components/checkboxes/fixtures.json +96 -93
  357. data/node_modules/govuk-frontend/govuk/components/cookie-banner/fixtures.json +46 -46
  358. data/node_modules/govuk-frontend/govuk/components/cookie-banner/template.njk +1 -1
  359. data/node_modules/govuk-frontend/govuk/components/date-input/fixtures.json +50 -50
  360. data/node_modules/govuk-frontend/govuk/components/details/details.js +797 -766
  361. data/node_modules/govuk-frontend/govuk/components/details/details.js.map +1 -0
  362. data/node_modules/govuk-frontend/govuk/components/details/fixtures.json +20 -20
  363. data/node_modules/govuk-frontend/govuk/components/error-message/fixtures.json +20 -20
  364. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +1089 -814
  365. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js.map +1 -0
  366. data/node_modules/govuk-frontend/govuk/components/error-summary/fixtures.json +44 -35
  367. data/node_modules/govuk-frontend/govuk/components/error-summary/template.njk +25 -21
  368. data/node_modules/govuk-frontend/govuk/components/fieldset/fixtures.json +51 -39
  369. data/node_modules/govuk-frontend/govuk/components/file-upload/fixtures.json +26 -26
  370. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +1 -1
  371. data/node_modules/govuk-frontend/govuk/components/footer/fixtures.json +46 -46
  372. data/node_modules/govuk-frontend/govuk/components/footer/macro-options.json +2 -2
  373. data/node_modules/govuk-frontend/govuk/components/header/_index.scss +6 -0
  374. data/node_modules/govuk-frontend/govuk/components/header/fixtures.json +93 -38
  375. data/node_modules/govuk-frontend/govuk/components/header/header.js +646 -992
  376. data/node_modules/govuk-frontend/govuk/components/header/header.js.map +1 -0
  377. data/node_modules/govuk-frontend/govuk/components/header/macro-options.json +8 -2
  378. data/node_modules/govuk-frontend/govuk/components/header/template.njk +4 -2
  379. data/node_modules/govuk-frontend/govuk/components/hint/fixtures.json +12 -12
  380. data/node_modules/govuk-frontend/govuk/components/input/fixtures.json +80 -80
  381. data/node_modules/govuk-frontend/govuk/components/inset-text/fixtures.json +12 -12
  382. data/node_modules/govuk-frontend/govuk/components/label/fixtures.json +34 -34
  383. data/node_modules/govuk-frontend/govuk/components/notification-banner/fixtures.json +56 -46
  384. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +790 -532
  385. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js.map +1 -0
  386. data/node_modules/govuk-frontend/govuk/components/notification-banner/template.njk +1 -1
  387. data/node_modules/govuk-frontend/govuk/components/pagination/_index.scss +10 -7
  388. data/node_modules/govuk-frontend/govuk/components/pagination/fixtures.json +94 -26
  389. data/node_modules/govuk-frontend/govuk/components/pagination/template.njk +1 -1
  390. data/node_modules/govuk-frontend/govuk/components/panel/fixtures.json +18 -18
  391. data/node_modules/govuk-frontend/govuk/components/phase-banner/fixtures.json +14 -14
  392. data/node_modules/govuk-frontend/govuk/components/phase-banner/macro-options.json +1 -1
  393. data/node_modules/govuk-frontend/govuk/components/radios/fixtures.json +94 -91
  394. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +1110 -1079
  395. data/node_modules/govuk-frontend/govuk/components/radios/radios.js.map +1 -0
  396. data/node_modules/govuk-frontend/govuk/components/select/fixtures.json +32 -32
  397. data/node_modules/govuk-frontend/govuk/components/skip-link/fixtures.json +22 -20
  398. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +1017 -1008
  399. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js.map +1 -0
  400. data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +107 -0
  401. data/node_modules/govuk-frontend/govuk/components/summary-list/fixtures.json +345 -50
  402. data/node_modules/govuk-frontend/govuk/components/summary-list/macro-options.json +110 -0
  403. data/node_modules/govuk-frontend/govuk/components/summary-list/template.njk +72 -28
  404. data/node_modules/govuk-frontend/govuk/components/table/_index.scss +1 -1
  405. data/node_modules/govuk-frontend/govuk/components/table/fixtures.json +40 -40
  406. data/node_modules/govuk-frontend/govuk/components/tabs/fixtures.json +29 -29
  407. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +1389 -1233
  408. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js.map +1 -0
  409. data/node_modules/govuk-frontend/govuk/components/tag/fixtures.json +28 -28
  410. data/node_modules/govuk-frontend/govuk/components/textarea/fixtures.json +34 -34
  411. data/node_modules/govuk-frontend/govuk/components/warning-text/fixtures.json +14 -14
  412. data/node_modules/govuk-frontend/govuk/core/_section-break.scss +1 -1
  413. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +2 -2
  414. data/node_modules/govuk-frontend/govuk/helpers/_focused.scss +5 -0
  415. data/node_modules/govuk-frontend/govuk/helpers/_links.scss +6 -6
  416. data/node_modules/govuk-frontend/govuk/i18n.js +389 -0
  417. data/node_modules/govuk-frontend/govuk/i18n.js.map +1 -0
  418. data/node_modules/govuk-frontend/govuk/macros/i18n.njk +15 -0
  419. data/node_modules/govuk-frontend/govuk/settings/_all.scss +1 -0
  420. data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +12 -0
  421. data/node_modules/govuk-frontend/govuk/settings/_compatibility.scss +26 -0
  422. data/node_modules/govuk-frontend/govuk/settings/_measurements.scss +5 -5
  423. data/node_modules/govuk-frontend/govuk/settings/_typography-font.scss +23 -0
  424. data/node_modules/govuk-frontend/govuk/settings/_typography-responsive.scss +12 -0
  425. data/node_modules/govuk-frontend/govuk/settings/_warnings.scss +53 -0
  426. data/node_modules/govuk-frontend/govuk/tools/_compatibility.scss +20 -6
  427. data/node_modules/govuk-frontend/govuk/vendor/polyfills/DOMTokenList.js +242 -241
  428. data/node_modules/govuk-frontend/govuk/vendor/polyfills/DOMTokenList.js.map +1 -0
  429. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +22 -0
  430. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js.map +1 -0
  431. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Document.js +17 -16
  432. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Document.js.map +1 -0
  433. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList.js +547 -546
  434. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList.js.map +1 -0
  435. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/closest.js +37 -36
  436. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/closest.js.map +1 -0
  437. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +301 -0
  438. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js.map +1 -0
  439. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/matches.js +21 -20
  440. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/matches.js.map +1 -0
  441. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/nextElementSibling.js +198 -197
  442. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/nextElementSibling.js.map +1 -0
  443. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/previousElementSibling.js +198 -197
  444. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/previousElementSibling.js.map +1 -0
  445. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element.js +106 -105
  446. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element.js.map +1 -0
  447. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Event.js +400 -399
  448. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Event.js.map +1 -0
  449. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Function/prototype/bind.js +239 -238
  450. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Function/prototype/bind.js.map +1 -0
  451. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Object/defineProperty.js +72 -71
  452. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Object/defineProperty.js.map +1 -0
  453. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +22 -0
  454. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js.map +1 -0
  455. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Window.js +17 -16
  456. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Window.js.map +1 -0
  457. data/node_modules/govuk-frontend/govuk-esm/all.mjs +52 -29
  458. data/node_modules/govuk-frontend/govuk-esm/common/closest-attribute-value.mjs +15 -0
  459. data/node_modules/govuk-frontend/govuk-esm/common/index.mjs +166 -0
  460. data/node_modules/govuk-frontend/govuk-esm/common/normalise-dataset.mjs +60 -0
  461. data/node_modules/govuk-frontend/govuk-esm/common.mjs +6 -28
  462. data/node_modules/govuk-frontend/govuk-esm/components/accordion/accordion.mjs +238 -85
  463. data/node_modules/govuk-frontend/govuk-esm/components/button/button.mjs +73 -34
  464. data/node_modules/govuk-frontend/govuk-esm/components/character-count/character-count.mjs +329 -124
  465. data/node_modules/govuk-frontend/govuk-esm/components/checkboxes/checkboxes.mjs +32 -20
  466. data/node_modules/govuk-frontend/govuk-esm/components/details/details.mjs +41 -20
  467. data/node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs +60 -14
  468. data/node_modules/govuk-frontend/govuk-esm/components/header/header.mjs +9 -2
  469. data/node_modules/govuk-frontend/govuk-esm/components/notification-banner/notification-banner.mjs +33 -4
  470. data/node_modules/govuk-frontend/govuk-esm/components/radios/radios.mjs +18 -11
  471. data/node_modules/govuk-frontend/govuk-esm/components/skip-link/skip-link.mjs +15 -7
  472. data/node_modules/govuk-frontend/govuk-esm/components/tabs/tabs.mjs +171 -38
  473. data/node_modules/govuk-frontend/govuk-esm/i18n.mjs +378 -0
  474. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Date/now.mjs +13 -0
  475. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/dataset.mjs +68 -0
  476. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/String/prototype/trim.mjs +13 -0
  477. data/node_modules/govuk-frontend/govuk-prototype-kit/init.js +7 -0
  478. data/node_modules/govuk-frontend/govuk-prototype-kit/init.scss +12 -0
  479. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +138 -7
  480. data/node_modules/govuk-frontend/package.json +1 -1
  481. metadata +70 -19
  482. data/app/assets/stylesheets/component_guide/print.scss +0 -1
  483. data/app/assets/stylesheets/govuk_publishing_components/_all_components_print.scss +0 -18
  484. data/app/assets/stylesheets/govuk_publishing_components/components/print/_accordion.scss +0 -28
  485. data/app/assets/stylesheets/govuk_publishing_components/components/print/_button.scss +0 -8
  486. data/app/assets/stylesheets/govuk_publishing_components/components/print/_cards.scss +0 -4
  487. data/app/assets/stylesheets/govuk_publishing_components/components/print/_contents-list.scss +0 -19
  488. data/app/assets/stylesheets/govuk_publishing_components/components/print/_emergency-banner.scss +0 -3
  489. data/app/assets/stylesheets/govuk_publishing_components/components/print/_govspeak-html-publication.scss +0 -5
  490. data/app/assets/stylesheets/govuk_publishing_components/components/print/_govspeak.scss +0 -80
  491. data/app/assets/stylesheets/govuk_publishing_components/components/print/_layout-super-navigation-header.scss +0 -3
  492. data/app/assets/stylesheets/govuk_publishing_components/components/print/_organisation-logo.scss +0 -7
  493. data/app/assets/stylesheets/govuk_publishing_components/components/print/_step-by-step-nav-header.scss +0 -7
  494. data/app/assets/stylesheets/govuk_publishing_components/components/print/_step-by-step-nav.scss +0 -116
  495. data/app/assets/stylesheets/govuk_publishing_components/components/print/_textarea.scss +0 -13
  496. data/app/assets/stylesheets/govuk_publishing_components/components/print/_title.scss +0 -7
@@ -1,1265 +1,2187 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
- typeof define === 'function' && define.amd ? define('GOVUKFrontend.CharacterCount', factory) :
4
- (global.GOVUKFrontend = global.GOVUKFrontend || {}, global.GOVUKFrontend.CharacterCount = factory());
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define('GOVUKFrontend.CharacterCount', factory) :
4
+ (global.GOVUKFrontend = global.GOVUKFrontend || {}, global.GOVUKFrontend.CharacterCount = factory());
5
5
  }(this, (function () { 'use strict';
6
6
 
7
- (function(undefined) {
8
-
9
- // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Object/defineProperty/detect.js
10
- var detect = (
11
- // In IE8, defineProperty could only act on DOM elements, so full support
12
- // for the feature requires the ability to set a property on an arbitrary object
13
- 'defineProperty' in Object && (function() {
14
- try {
15
- var a = {};
16
- Object.defineProperty(a, 'test', {value:42});
17
- return true;
18
- } catch(e) {
19
- return false
20
- }
21
- }())
22
- );
23
-
24
- if (detect) return
25
-
26
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Object.defineProperty&flags=always
27
- (function (nativeDefineProperty) {
28
-
29
- var supportsAccessors = Object.prototype.hasOwnProperty('__defineGetter__');
30
- var ERR_ACCESSORS_NOT_SUPPORTED = 'Getters & setters cannot be defined on this javascript engine';
31
- var ERR_VALUE_ACCESSORS = 'A property cannot both have accessors and be writable or have a value';
32
-
33
- Object.defineProperty = function defineProperty(object, property, descriptor) {
34
-
35
- // Where native support exists, assume it
36
- if (nativeDefineProperty && (object === window || object === document || object === Element.prototype || object instanceof Element)) {
37
- return nativeDefineProperty(object, property, descriptor);
38
- }
39
-
40
- if (object === null || !(object instanceof Object || typeof object === 'object')) {
41
- throw new TypeError('Object.defineProperty called on non-object');
42
- }
43
-
44
- if (!(descriptor instanceof Object)) {
45
- throw new TypeError('Property description must be an object');
46
- }
47
-
48
- var propertyString = String(property);
49
- var hasValueOrWritable = 'value' in descriptor || 'writable' in descriptor;
50
- var getterType = 'get' in descriptor && typeof descriptor.get;
51
- var setterType = 'set' in descriptor && typeof descriptor.set;
52
-
53
- // handle descriptor.get
54
- if (getterType) {
55
- if (getterType !== 'function') {
56
- throw new TypeError('Getter must be a function');
57
- }
58
- if (!supportsAccessors) {
59
- throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
60
- }
61
- if (hasValueOrWritable) {
62
- throw new TypeError(ERR_VALUE_ACCESSORS);
63
- }
64
- Object.__defineGetter__.call(object, propertyString, descriptor.get);
65
- } else {
66
- object[propertyString] = descriptor.value;
67
- }
68
-
69
- // handle descriptor.set
70
- if (setterType) {
71
- if (setterType !== 'function') {
72
- throw new TypeError('Setter must be a function');
73
- }
74
- if (!supportsAccessors) {
75
- throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
76
- }
77
- if (hasValueOrWritable) {
78
- throw new TypeError(ERR_VALUE_ACCESSORS);
79
- }
80
- Object.__defineSetter__.call(object, propertyString, descriptor.set);
81
- }
82
-
83
- // OK to define value unconditionally - if a getter has been specified as well, an error would be thrown above
84
- if ('value' in descriptor) {
85
- object[propertyString] = descriptor.value;
86
- }
87
-
88
- return object;
89
- };
90
- }(Object.defineProperty));
91
- })
92
- .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
93
-
94
- (function(undefined) {
95
- // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Function/prototype/bind/detect.js
96
- var detect = 'bind' in Function.prototype;
7
+ (function(undefined) {
97
8
 
98
- if (detect) return
9
+ // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/1f3c09b402f65bf6e393f933a15ba63f1b86ef1f/packages/polyfill-library/polyfills/Element/prototype/matches/detect.js
10
+ var detect = (
11
+ 'document' in this && "matches" in document.documentElement
12
+ );
99
13
 
100
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Function.prototype.bind&flags=always
101
- Object.defineProperty(Function.prototype, 'bind', {
102
- value: function bind(that) { // .length is 1
103
- // add necessary es5-shim utilities
104
- var $Array = Array;
105
- var $Object = Object;
106
- var ObjectPrototype = $Object.prototype;
107
- var ArrayPrototype = $Array.prototype;
108
- var Empty = function Empty() {};
109
- var to_string = ObjectPrototype.toString;
110
- var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
111
- var isCallable; /* inlined from https://npmjs.com/is-callable */ var fnToStr = Function.prototype.toString, tryFunctionObject = function tryFunctionObject(value) { try { fnToStr.call(value); return true; } catch (e) { return false; } }, fnClass = '[object Function]', genClass = '[object GeneratorFunction]'; isCallable = function isCallable(value) { if (typeof value !== 'function') { return false; } if (hasToStringTag) { return tryFunctionObject(value); } var strClass = to_string.call(value); return strClass === fnClass || strClass === genClass; };
112
- var array_slice = ArrayPrototype.slice;
113
- var array_concat = ArrayPrototype.concat;
114
- var array_push = ArrayPrototype.push;
115
- var max = Math.max;
116
- // /add necessary es5-shim utilities
117
-
118
- // 1. Let Target be the this value.
119
- var target = this;
120
- // 2. If IsCallable(Target) is false, throw a TypeError exception.
121
- if (!isCallable(target)) {
122
- throw new TypeError('Function.prototype.bind called on incompatible ' + target);
123
- }
124
- // 3. Let A be a new (possibly empty) internal list of all of the
125
- // argument values provided after thisArg (arg1, arg2 etc), in order.
126
- // XXX slicedArgs will stand in for "A" if used
127
- var args = array_slice.call(arguments, 1); // for normal call
128
- // 4. Let F be a new native ECMAScript object.
129
- // 11. Set the [[Prototype]] internal property of F to the standard
130
- // built-in Function prototype object as specified in 15.3.3.1.
131
- // 12. Set the [[Call]] internal property of F as described in
132
- // 15.3.4.5.1.
133
- // 13. Set the [[Construct]] internal property of F as described in
134
- // 15.3.4.5.2.
135
- // 14. Set the [[HasInstance]] internal property of F as described in
136
- // 15.3.4.5.3.
137
- var bound;
138
- var binder = function () {
139
-
140
- if (this instanceof bound) {
141
- // 15.3.4.5.2 [[Construct]]
142
- // When the [[Construct]] internal method of a function object,
143
- // F that was created using the bind function is called with a
144
- // list of arguments ExtraArgs, the following steps are taken:
145
- // 1. Let target be the value of F's [[TargetFunction]]
146
- // internal property.
147
- // 2. If target has no [[Construct]] internal method, a
148
- // TypeError exception is thrown.
149
- // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
150
- // property.
151
- // 4. Let args be a new list containing the same values as the
152
- // list boundArgs in the same order followed by the same
153
- // values as the list ExtraArgs in the same order.
154
- // 5. Return the result of calling the [[Construct]] internal
155
- // method of target providing args as the arguments.
156
-
157
- var result = target.apply(
158
- this,
159
- array_concat.call(args, array_slice.call(arguments))
160
- );
161
- if ($Object(result) === result) {
162
- return result;
163
- }
164
- return this;
165
-
166
- } else {
167
- // 15.3.4.5.1 [[Call]]
168
- // When the [[Call]] internal method of a function object, F,
169
- // which was created using the bind function is called with a
170
- // this value and a list of arguments ExtraArgs, the following
171
- // steps are taken:
172
- // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
173
- // property.
174
- // 2. Let boundThis be the value of F's [[BoundThis]] internal
175
- // property.
176
- // 3. Let target be the value of F's [[TargetFunction]] internal
177
- // property.
178
- // 4. Let args be a new list containing the same values as the
179
- // list boundArgs in the same order followed by the same
180
- // values as the list ExtraArgs in the same order.
181
- // 5. Return the result of calling the [[Call]] internal method
182
- // of target providing boundThis as the this value and
183
- // providing args as the arguments.
184
-
185
- // equiv: target.call(this, ...boundArgs, ...args)
186
- return target.apply(
187
- that,
188
- array_concat.call(args, array_slice.call(arguments))
189
- );
190
-
191
- }
14
+ if (detect) return
192
15
 
193
- };
16
+ // Polyfill from https://raw.githubusercontent.com/Financial-Times/polyfill-service/1f3c09b402f65bf6e393f933a15ba63f1b86ef1f/packages/polyfill-library/polyfills/Element/prototype/matches/polyfill.js
17
+ Element.prototype.matches = Element.prototype.webkitMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector || function matches(selector) {
18
+ var element = this;
19
+ var elements = (element.document || element.ownerDocument).querySelectorAll(selector);
20
+ var index = 0;
194
21
 
195
- // 15. If the [[Class]] internal property of Target is "Function", then
196
- // a. Let L be the length property of Target minus the length of A.
197
- // b. Set the length own property of F to either 0 or L, whichever is
198
- // larger.
199
- // 16. Else set the length own property of F to 0.
22
+ while (elements[index] && elements[index] !== element) {
23
+ ++index;
24
+ }
200
25
 
201
- var boundLength = max(0, target.length - args.length);
26
+ return !!elements[index];
27
+ };
202
28
 
203
- // 17. Set the attributes of the length own property of F to the values
204
- // specified in 15.3.5.1.
205
- var boundArgs = [];
206
- for (var i = 0; i < boundLength; i++) {
207
- array_push.call(boundArgs, '$' + i);
208
- }
29
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
209
30
 
210
- // XXX Build a dynamic function with desired amount of arguments is the only
211
- // way to set the length property of a function.
212
- // In environments where Content Security Policies enabled (Chrome extensions,
213
- // for ex.) all use of eval or Function costructor throws an exception.
214
- // However in all of these environments Function.prototype.bind exists
215
- // and so this code will never be executed.
216
- bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
217
-
218
- if (target.prototype) {
219
- Empty.prototype = target.prototype;
220
- bound.prototype = new Empty();
221
- // Clean up dangling references.
222
- Empty.prototype = null;
31
+ (function(undefined) {
32
+
33
+ // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/1f3c09b402f65bf6e393f933a15ba63f1b86ef1f/packages/polyfill-library/polyfills/Element/prototype/closest/detect.js
34
+ var detect = (
35
+ 'document' in this && "closest" in document.documentElement
36
+ );
37
+
38
+ if (detect) return
39
+
40
+ // Polyfill from https://raw.githubusercontent.com/Financial-Times/polyfill-service/1f3c09b402f65bf6e393f933a15ba63f1b86ef1f/packages/polyfill-library/polyfills/Element/prototype/closest/polyfill.js
41
+ Element.prototype.closest = function closest(selector) {
42
+ var node = this;
43
+
44
+ while (node) {
45
+ if (node.matches(selector)) return node;
46
+ else node = 'SVGElement' in window && node instanceof SVGElement ? node.parentNode : node.parentElement;
47
+ }
48
+
49
+ return null;
50
+ };
51
+
52
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
53
+
54
+ /**
55
+ * Returns the value of the given attribute closest to the given element (including itself)
56
+ *
57
+ * @param {HTMLElement} $element - The element to start walking the DOM tree up
58
+ * @param {string} attributeName - The name of the attribute
59
+ * @returns {string | undefined} Attribute value
60
+ */
61
+ function closestAttributeValue ($element, attributeName) {
62
+ var closestElementWithAttribute = $element.closest('[' + attributeName + ']');
63
+ if (closestElementWithAttribute) {
64
+ return closestElementWithAttribute.getAttribute(attributeName)
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Common helpers which do not require polyfill.
70
+ *
71
+ * IMPORTANT: If a helper require a polyfill, please isolate it in its own module
72
+ * so that the polyfill can be properly tree-shaken and does not burden
73
+ * the components that do not need that helper
74
+ *
75
+ * @module common/index
76
+ */
77
+
78
+ /**
79
+ * Config flattening function
80
+ *
81
+ * Takes any number of objects, flattens them into namespaced key-value pairs,
82
+ * (e.g. {'i18n.showSection': 'Show section'}) and combines them together, with
83
+ * greatest priority on the LAST item passed in.
84
+ *
85
+ * @returns {Object<string, unknown>} A flattened object of key-value pairs.
86
+ */
87
+ function mergeConfigs (/* configObject1, configObject2, ...configObjects */) {
88
+ /**
89
+ * Function to take nested objects and flatten them to a dot-separated keyed
90
+ * object. Doing this means we don't need to do any deep/recursive merging of
91
+ * each of our objects, nor transform our dataset from a flat list into a
92
+ * nested object.
93
+ *
94
+ * @param {Object<string, unknown>} configObject - Deeply nested object
95
+ * @returns {Object<string, unknown>} Flattened object with dot-separated keys
96
+ */
97
+ var flattenObject = function (configObject) {
98
+ // Prepare an empty return object
99
+ var flattenedObject = {};
100
+
101
+ /**
102
+ * Our flattening function, this is called recursively for each level of
103
+ * depth in the object. At each level we prepend the previous level names to
104
+ * the key using `prefix`.
105
+ *
106
+ * @param {Partial<Object<string, unknown>>} obj - Object to flatten
107
+ * @param {string} [prefix] - Optional dot-separated prefix
108
+ */
109
+ var flattenLoop = function (obj, prefix) {
110
+ // Loop through keys...
111
+ for (var key in obj) {
112
+ // Check to see if this is a prototypical key/value,
113
+ // if it is, skip it.
114
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) {
115
+ continue
223
116
  }
117
+ var value = obj[key];
118
+ var prefixedKey = prefix ? prefix + '.' + key : key;
119
+ if (typeof value === 'object') {
120
+ // If the value is a nested object, recurse over that too
121
+ flattenLoop(value, prefixedKey);
122
+ } else {
123
+ // Otherwise, add this value to our return object
124
+ flattenedObject[prefixedKey] = value;
125
+ }
126
+ }
127
+ };
128
+
129
+ // Kick off the recursive loop
130
+ flattenLoop(configObject);
131
+ return flattenedObject
132
+ };
133
+
134
+ // Start with an empty object as our base
135
+ var formattedConfigObject = {};
136
+
137
+ // Loop through each of the remaining passed objects and push their keys
138
+ // one-by-one into configObject. Any duplicate keys will override the existing
139
+ // key with the new value.
140
+ for (var i = 0; i < arguments.length; i++) {
141
+ var obj = flattenObject(arguments[i]);
142
+ for (var key in obj) {
143
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
144
+ formattedConfigObject[key] = obj[key];
145
+ }
146
+ }
147
+ }
148
+
149
+ return formattedConfigObject
150
+ }
224
151
 
225
- // TODO
226
- // 18. Set the [[Extensible]] internal property of F to true.
227
-
228
- // TODO
229
- // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
230
- // 20. Call the [[DefineOwnProperty]] internal method of F with
231
- // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
232
- // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
233
- // false.
234
- // 21. Call the [[DefineOwnProperty]] internal method of F with
235
- // arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
236
- // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
237
- // and false.
238
-
239
- // TODO
240
- // NOTE Function objects created using Function.prototype.bind do not
241
- // have a prototype property or the [[Code]], [[FormalParameters]], and
242
- // [[Scope]] internal properties.
243
- // XXX can't delete prototype in pure-js.
244
-
245
- // 22. Return F.
246
- return bound;
152
+ /**
153
+ * Extracts keys starting with a particular namespace from a flattened config
154
+ * object, removing the namespace in the process.
155
+ *
156
+ * @param {Object<string, unknown>} configObject - The object to extract key-value pairs from.
157
+ * @param {string} namespace - The namespace to filter keys with.
158
+ * @returns {Object<string, unknown>} Flattened object with dot-separated key namespace removed
159
+ * @throws {Error} Config object required
160
+ * @throws {Error} Namespace string required
161
+ */
162
+ function extractConfigByNamespace (configObject, namespace) {
163
+ // Check we have what we need
164
+ if (!configObject || typeof configObject !== 'object') {
165
+ throw new Error('Provide a `configObject` of type "object".')
166
+ }
167
+ if (!namespace || typeof namespace !== 'string') {
168
+ throw new Error('Provide a `namespace` of type "string" to filter the `configObject` by.')
169
+ }
170
+ var newObject = {};
171
+ for (var key in configObject) {
172
+ // Split the key into parts, using . as our namespace separator
173
+ var keyParts = key.split('.');
174
+ // Check if the first namespace matches the configured namespace
175
+ if (Object.prototype.hasOwnProperty.call(configObject, key) && keyParts[0] === namespace) {
176
+ // Remove the first item (the namespace) from the parts array,
177
+ // but only if there is more than one part (we don't want blank keys!)
178
+ if (keyParts.length > 1) {
179
+ keyParts.shift();
180
+ }
181
+ // Join the remaining parts back together
182
+ var newKey = keyParts.join('.');
183
+ // Add them to our new object
184
+ newObject[newKey] = configObject[key];
247
185
  }
248
- });
249
- })
250
- .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
186
+ }
187
+ return newObject
188
+ }
251
189
 
252
- (function(undefined) {
190
+ /**
191
+ * @callback nodeListIterator
192
+ * @param {Element} value - The current node being iterated on
193
+ * @param {number} index - The current index in the iteration
194
+ * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
195
+ * @returns {void}
196
+ */
197
+
198
+ (function(undefined) {
199
+
200
+ // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Object/defineProperty/detect.js
201
+ var detect = (
202
+ // In IE8, defineProperty could only act on DOM elements, so full support
203
+ // for the feature requires the ability to set a property on an arbitrary object
204
+ 'defineProperty' in Object && (function() {
205
+ try {
206
+ var a = {};
207
+ Object.defineProperty(a, 'test', {value:42});
208
+ return true;
209
+ } catch(e) {
210
+ return false
211
+ }
212
+ }())
213
+ );
253
214
 
254
- // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Window/detect.js
255
- var detect = ('Window' in this);
215
+ if (detect) return
256
216
 
257
- if (detect) return
217
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Object.defineProperty&flags=always
218
+ (function (nativeDefineProperty) {
219
+
220
+ var supportsAccessors = Object.prototype.hasOwnProperty('__defineGetter__');
221
+ var ERR_ACCESSORS_NOT_SUPPORTED = 'Getters & setters cannot be defined on this javascript engine';
222
+ var ERR_VALUE_ACCESSORS = 'A property cannot both have accessors and be writable or have a value';
223
+
224
+ Object.defineProperty = function defineProperty(object, property, descriptor) {
225
+
226
+ // Where native support exists, assume it
227
+ if (nativeDefineProperty && (object === window || object === document || object === Element.prototype || object instanceof Element)) {
228
+ return nativeDefineProperty(object, property, descriptor);
229
+ }
230
+
231
+ if (object === null || !(object instanceof Object || typeof object === 'object')) {
232
+ throw new TypeError('Object.defineProperty called on non-object');
233
+ }
234
+
235
+ if (!(descriptor instanceof Object)) {
236
+ throw new TypeError('Property description must be an object');
237
+ }
238
+
239
+ var propertyString = String(property);
240
+ var hasValueOrWritable = 'value' in descriptor || 'writable' in descriptor;
241
+ var getterType = 'get' in descriptor && typeof descriptor.get;
242
+ var setterType = 'set' in descriptor && typeof descriptor.set;
243
+
244
+ // handle descriptor.get
245
+ if (getterType) {
246
+ if (getterType !== 'function') {
247
+ throw new TypeError('Getter must be a function');
248
+ }
249
+ if (!supportsAccessors) {
250
+ throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
251
+ }
252
+ if (hasValueOrWritable) {
253
+ throw new TypeError(ERR_VALUE_ACCESSORS);
254
+ }
255
+ Object.__defineGetter__.call(object, propertyString, descriptor.get);
256
+ } else {
257
+ object[propertyString] = descriptor.value;
258
+ }
259
+
260
+ // handle descriptor.set
261
+ if (setterType) {
262
+ if (setterType !== 'function') {
263
+ throw new TypeError('Setter must be a function');
264
+ }
265
+ if (!supportsAccessors) {
266
+ throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
267
+ }
268
+ if (hasValueOrWritable) {
269
+ throw new TypeError(ERR_VALUE_ACCESSORS);
270
+ }
271
+ Object.__defineSetter__.call(object, propertyString, descriptor.set);
272
+ }
273
+
274
+ // OK to define value unconditionally - if a getter has been specified as well, an error would be thrown above
275
+ if ('value' in descriptor) {
276
+ object[propertyString] = descriptor.value;
277
+ }
278
+
279
+ return object;
280
+ };
281
+ }(Object.defineProperty));
282
+ })
283
+ .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
284
+
285
+ (function(undefined) {
286
+
287
+ // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Document/detect.js
288
+ var detect = ("Document" in this);
258
289
 
259
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Window&flags=always
260
- if ((typeof WorkerGlobalScope === "undefined") && (typeof importScripts !== "function")) {
261
- (function (global) {
262
- if (global.constructor) {
263
- global.Window = global.constructor;
264
- } else {
265
- (global.Window = global.constructor = new Function('return function Window() {}')()).prototype = this;
266
- }
267
- }(this));
268
- }
290
+ if (detect) return
269
291
 
270
- })
271
- .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
292
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Document&flags=always
293
+ if ((typeof WorkerGlobalScope === "undefined") && (typeof importScripts !== "function")) {
272
294
 
273
- (function(undefined) {
295
+ if (this.HTMLDocument) { // IE8
274
296
 
275
- // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Document/detect.js
276
- var detect = ("Document" in this);
297
+ // HTMLDocument is an extension of Document. If the browser has HTMLDocument but not Document, the former will suffice as an alias for the latter.
298
+ this.Document = this.HTMLDocument;
277
299
 
278
- if (detect) return
300
+ } else {
279
301
 
280
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Document&flags=always
281
- if ((typeof WorkerGlobalScope === "undefined") && (typeof importScripts !== "function")) {
302
+ // Create an empty function to act as the missing constructor for the document object, attach the document object as its prototype. The function needs to be anonymous else it is hoisted and causes the feature detect to prematurely pass, preventing the assignments below being made.
303
+ this.Document = this.HTMLDocument = document.constructor = (new Function('return function Document() {}')());
304
+ this.Document.prototype = document;
305
+ }
306
+ }
282
307
 
283
- if (this.HTMLDocument) { // IE8
284
308
 
285
- // HTMLDocument is an extension of Document. If the browser has HTMLDocument but not Document, the former will suffice as an alias for the latter.
286
- this.Document = this.HTMLDocument;
309
+ })
310
+ .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
287
311
 
288
- } else {
312
+ (function(undefined) {
289
313
 
290
- // Create an empty function to act as the missing constructor for the document object, attach the document object as its prototype. The function needs to be anonymous else it is hoisted and causes the feature detect to prematurely pass, preventing the assignments below being made.
291
- this.Document = this.HTMLDocument = document.constructor = (new Function('return function Document() {}')());
292
- this.Document.prototype = document;
293
- }
294
- }
295
-
296
-
297
- })
298
- .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
299
-
300
- (function(undefined) {
301
-
302
- // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Element/detect.js
303
- var detect = ('Element' in this && 'HTMLElement' in this);
304
-
305
- if (detect) return
306
-
307
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Element&flags=always
308
- (function () {
309
-
310
- // IE8
311
- if (window.Element && !window.HTMLElement) {
312
- window.HTMLElement = window.Element;
313
- return;
314
- }
315
-
316
- // create Element constructor
317
- window.Element = window.HTMLElement = new Function('return function Element() {}')();
318
-
319
- // generate sandboxed iframe
320
- var vbody = document.appendChild(document.createElement('body'));
321
- var frame = vbody.appendChild(document.createElement('iframe'));
322
-
323
- // use sandboxed iframe to replicate Element functionality
324
- var frameDocument = frame.contentWindow.document;
325
- var prototype = Element.prototype = frameDocument.appendChild(frameDocument.createElement('*'));
326
- var cache = {};
327
-
328
- // polyfill Element.prototype on an element
329
- var shiv = function (element, deep) {
330
- var
331
- childNodes = element.childNodes || [],
332
- index = -1,
333
- key, value, childNode;
334
-
335
- if (element.nodeType === 1 && element.constructor !== Element) {
336
- element.constructor = Element;
337
-
338
- for (key in cache) {
339
- value = cache[key];
340
- element[key] = value;
341
- }
342
- }
343
-
344
- while (childNode = deep && childNodes[++index]) {
345
- shiv(childNode, deep);
346
- }
347
-
348
- return element;
349
- };
350
-
351
- var elements = document.getElementsByTagName('*');
352
- var nativeCreateElement = document.createElement;
353
- var interval;
354
- var loopLimit = 100;
355
-
356
- prototype.attachEvent('onpropertychange', function (event) {
357
- var
358
- propertyName = event.propertyName,
359
- nonValue = !cache.hasOwnProperty(propertyName),
360
- newValue = prototype[propertyName],
361
- oldValue = cache[propertyName],
362
- index = -1,
363
- element;
364
-
365
- while (element = elements[++index]) {
366
- if (element.nodeType === 1) {
367
- if (nonValue || element[propertyName] === oldValue) {
368
- element[propertyName] = newValue;
369
- }
370
- }
371
- }
372
-
373
- cache[propertyName] = newValue;
374
- });
375
-
376
- prototype.constructor = Element;
377
-
378
- if (!prototype.hasAttribute) {
379
- // <Element>.hasAttribute
380
- prototype.hasAttribute = function hasAttribute(name) {
381
- return this.getAttribute(name) !== null;
382
- };
383
- }
384
-
385
- // Apply Element prototype to the pre-existing DOM as soon as the body element appears.
386
- function bodyCheck() {
387
- if (!(loopLimit--)) clearTimeout(interval);
388
- if (document.body && !document.body.prototype && /(complete|interactive)/.test(document.readyState)) {
389
- shiv(document, true);
390
- if (interval && document.body.prototype) clearTimeout(interval);
391
- return (!!document.body.prototype);
392
- }
393
- return false;
394
- }
395
- if (!bodyCheck()) {
396
- document.onreadystatechange = bodyCheck;
397
- interval = setInterval(bodyCheck, 25);
398
- }
399
-
400
- // Apply to any new elements created after load
401
- document.createElement = function createElement(nodeName) {
402
- var element = nativeCreateElement(String(nodeName).toLowerCase());
403
- return shiv(element);
404
- };
405
-
406
- // remove sandboxed iframe
407
- document.removeChild(vbody);
408
- }());
409
-
410
- })
411
- .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
412
-
413
- (function(undefined) {
414
-
415
- // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Event/detect.js
416
- var detect = (
417
- (function(global) {
418
-
419
- if (!('Event' in global)) return false;
420
- if (typeof global.Event === 'function') return true;
421
-
422
- try {
423
-
424
- // In IE 9-11, the Event object exists but cannot be instantiated
425
- new Event('click');
426
- return true;
427
- } catch(e) {
314
+ // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Element/detect.js
315
+ var detect = ('Element' in this && 'HTMLElement' in this);
316
+
317
+ if (detect) return
318
+
319
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Element&flags=always
320
+ (function () {
321
+
322
+ // IE8
323
+ if (window.Element && !window.HTMLElement) {
324
+ window.HTMLElement = window.Element;
325
+ return;
326
+ }
327
+
328
+ // create Element constructor
329
+ window.Element = window.HTMLElement = new Function('return function Element() {}')();
330
+
331
+ // generate sandboxed iframe
332
+ var vbody = document.appendChild(document.createElement('body'));
333
+ var frame = vbody.appendChild(document.createElement('iframe'));
334
+
335
+ // use sandboxed iframe to replicate Element functionality
336
+ var frameDocument = frame.contentWindow.document;
337
+ var prototype = Element.prototype = frameDocument.appendChild(frameDocument.createElement('*'));
338
+ var cache = {};
339
+
340
+ // polyfill Element.prototype on an element
341
+ var shiv = function (element, deep) {
342
+ var
343
+ childNodes = element.childNodes || [],
344
+ index = -1,
345
+ key, value, childNode;
346
+
347
+ if (element.nodeType === 1 && element.constructor !== Element) {
348
+ element.constructor = Element;
349
+
350
+ for (key in cache) {
351
+ value = cache[key];
352
+ element[key] = value;
353
+ }
354
+ }
355
+
356
+ while (childNode = deep && childNodes[++index]) {
357
+ shiv(childNode, deep);
358
+ }
359
+
360
+ return element;
361
+ };
362
+
363
+ var elements = document.getElementsByTagName('*');
364
+ var nativeCreateElement = document.createElement;
365
+ var interval;
366
+ var loopLimit = 100;
367
+
368
+ prototype.attachEvent('onpropertychange', function (event) {
369
+ var
370
+ propertyName = event.propertyName,
371
+ nonValue = !cache.hasOwnProperty(propertyName),
372
+ newValue = prototype[propertyName],
373
+ oldValue = cache[propertyName],
374
+ index = -1,
375
+ element;
376
+
377
+ while (element = elements[++index]) {
378
+ if (element.nodeType === 1) {
379
+ if (nonValue || element[propertyName] === oldValue) {
380
+ element[propertyName] = newValue;
381
+ }
382
+ }
383
+ }
384
+
385
+ cache[propertyName] = newValue;
386
+ });
387
+
388
+ prototype.constructor = Element;
389
+
390
+ if (!prototype.hasAttribute) {
391
+ // <Element>.hasAttribute
392
+ prototype.hasAttribute = function hasAttribute(name) {
393
+ return this.getAttribute(name) !== null;
394
+ };
395
+ }
396
+
397
+ // Apply Element prototype to the pre-existing DOM as soon as the body element appears.
398
+ function bodyCheck() {
399
+ if (!(loopLimit--)) clearTimeout(interval);
400
+ if (document.body && !document.body.prototype && /(complete|interactive)/.test(document.readyState)) {
401
+ shiv(document, true);
402
+ if (interval && document.body.prototype) clearTimeout(interval);
403
+ return (!!document.body.prototype);
404
+ }
428
405
  return false;
429
406
  }
430
- }(this))
431
- );
432
-
433
- if (detect) return
434
-
435
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Event&flags=always
436
- (function () {
437
- var unlistenableWindowEvents = {
438
- click: 1,
439
- dblclick: 1,
440
- keyup: 1,
441
- keypress: 1,
442
- keydown: 1,
443
- mousedown: 1,
444
- mouseup: 1,
445
- mousemove: 1,
446
- mouseover: 1,
447
- mouseenter: 1,
448
- mouseleave: 1,
449
- mouseout: 1,
450
- storage: 1,
451
- storagecommit: 1,
452
- textinput: 1
453
- };
454
-
455
- // This polyfill depends on availability of `document` so will not run in a worker
456
- // However, we asssume there are no browsers with worker support that lack proper
457
- // support for `Event` within the worker
458
- if (typeof document === 'undefined' || typeof window === 'undefined') return;
459
-
460
- function indexOf(array, element) {
461
- var
462
- index = -1,
463
- length = array.length;
464
-
465
- while (++index < length) {
466
- if (index in array && array[index] === element) {
467
- return index;
468
- }
469
- }
470
-
471
- return -1;
472
- }
473
-
474
- var existingProto = (window.Event && window.Event.prototype) || null;
475
- window.Event = Window.prototype.Event = function Event(type, eventInitDict) {
476
- if (!type) {
477
- throw new Error('Not enough arguments');
478
- }
479
-
480
- var event;
481
- // Shortcut if browser supports createEvent
482
- if ('createEvent' in document) {
483
- event = document.createEvent('Event');
484
- var bubbles = eventInitDict && eventInitDict.bubbles !== undefined ? eventInitDict.bubbles : false;
485
- var cancelable = eventInitDict && eventInitDict.cancelable !== undefined ? eventInitDict.cancelable : false;
486
-
487
- event.initEvent(type, bubbles, cancelable);
488
-
489
- return event;
490
- }
491
-
492
- event = document.createEventObject();
493
-
494
- event.type = type;
495
- event.bubbles = eventInitDict && eventInitDict.bubbles !== undefined ? eventInitDict.bubbles : false;
496
- event.cancelable = eventInitDict && eventInitDict.cancelable !== undefined ? eventInitDict.cancelable : false;
497
-
498
- return event;
499
- };
500
- if (existingProto) {
501
- Object.defineProperty(window.Event, 'prototype', {
502
- configurable: false,
503
- enumerable: false,
504
- writable: true,
505
- value: existingProto
506
- });
507
- }
508
-
509
- if (!('createEvent' in document)) {
510
- window.addEventListener = Window.prototype.addEventListener = Document.prototype.addEventListener = Element.prototype.addEventListener = function addEventListener() {
511
- var
512
- element = this,
513
- type = arguments[0],
514
- listener = arguments[1];
515
-
516
- if (element === window && type in unlistenableWindowEvents) {
517
- throw new Error('In IE8 the event: ' + type + ' is not available on the window object. Please see https://github.com/Financial-Times/polyfill-service/issues/317 for more information.');
518
- }
519
-
520
- if (!element._events) {
521
- element._events = {};
522
- }
523
-
524
- if (!element._events[type]) {
525
- element._events[type] = function (event) {
526
- var
527
- list = element._events[event.type].list,
528
- events = list.slice(),
529
- index = -1,
530
- length = events.length,
531
- eventElement;
532
-
533
- event.preventDefault = function preventDefault() {
534
- if (event.cancelable !== false) {
535
- event.returnValue = false;
536
- }
537
- };
538
-
539
- event.stopPropagation = function stopPropagation() {
540
- event.cancelBubble = true;
541
- };
542
-
543
- event.stopImmediatePropagation = function stopImmediatePropagation() {
544
- event.cancelBubble = true;
545
- event.cancelImmediate = true;
546
- };
547
-
548
- event.currentTarget = element;
549
- event.relatedTarget = event.fromElement || null;
550
- event.target = event.target || event.srcElement || element;
551
- event.timeStamp = new Date().getTime();
552
-
553
- if (event.clientX) {
554
- event.pageX = event.clientX + document.documentElement.scrollLeft;
555
- event.pageY = event.clientY + document.documentElement.scrollTop;
556
- }
557
-
558
- while (++index < length && !event.cancelImmediate) {
559
- if (index in events) {
560
- eventElement = events[index];
561
-
562
- if (indexOf(list, eventElement) !== -1 && typeof eventElement === 'function') {
563
- eventElement.call(element, event);
564
- }
565
- }
566
- }
567
- };
568
-
569
- element._events[type].list = [];
570
-
571
- if (element.attachEvent) {
572
- element.attachEvent('on' + type, element._events[type]);
573
- }
574
- }
575
-
576
- element._events[type].list.push(listener);
577
- };
578
-
579
- window.removeEventListener = Window.prototype.removeEventListener = Document.prototype.removeEventListener = Element.prototype.removeEventListener = function removeEventListener() {
580
- var
581
- element = this,
582
- type = arguments[0],
583
- listener = arguments[1],
584
- index;
585
-
586
- if (element._events && element._events[type] && element._events[type].list) {
587
- index = indexOf(element._events[type].list, listener);
588
-
589
- if (index !== -1) {
590
- element._events[type].list.splice(index, 1);
591
-
592
- if (!element._events[type].list.length) {
593
- if (element.detachEvent) {
594
- element.detachEvent('on' + type, element._events[type]);
595
- }
596
- delete element._events[type];
597
- }
598
- }
599
- }
600
- };
601
-
602
- window.dispatchEvent = Window.prototype.dispatchEvent = Document.prototype.dispatchEvent = Element.prototype.dispatchEvent = function dispatchEvent(event) {
603
- if (!arguments.length) {
604
- throw new Error('Not enough arguments');
605
- }
606
-
607
- if (!event || typeof event.type !== 'string') {
608
- throw new Error('DOM Events Exception 0');
609
- }
610
-
611
- var element = this, type = event.type;
612
-
613
- try {
614
- if (!event.bubbles) {
615
- event.cancelBubble = true;
616
-
617
- var cancelBubbleEvent = function (event) {
618
- event.cancelBubble = true;
619
-
620
- (element || window).detachEvent('on' + type, cancelBubbleEvent);
621
- };
622
-
623
- this.attachEvent('on' + type, cancelBubbleEvent);
624
- }
625
-
626
- this.fireEvent('on' + type, event);
627
- } catch (error) {
628
- event.target = element;
629
-
630
- do {
631
- event.currentTarget = element;
632
-
633
- if ('_events' in element && typeof element._events[type] === 'function') {
634
- element._events[type].call(element, event);
635
- }
636
-
637
- if (typeof element['on' + type] === 'function') {
638
- element['on' + type].call(element, event);
639
- }
640
-
641
- element = element.nodeType === 9 ? element.parentWindow : element.parentNode;
642
- } while (element && !event.cancelBubble);
643
- }
644
-
645
- return true;
646
- };
647
-
648
- // Add the DOMContentLoaded Event
649
- document.attachEvent('onreadystatechange', function() {
650
- if (document.readyState === 'complete') {
651
- document.dispatchEvent(new Event('DOMContentLoaded', {
652
- bubbles: true
653
- }));
654
- }
655
- });
656
- }
657
- }());
658
-
659
- })
660
- .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
661
-
662
- (function(undefined) {
663
-
664
- // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/master/packages/polyfill-library/polyfills/DOMTokenList/detect.js
665
- var detect = (
666
- 'DOMTokenList' in this && (function (x) {
667
- return 'classList' in x ? !x.classList.toggle('x', false) && !x.className : true;
668
- })(document.createElement('x'))
669
- );
407
+ if (!bodyCheck()) {
408
+ document.onreadystatechange = bodyCheck;
409
+ interval = setInterval(bodyCheck, 25);
410
+ }
411
+
412
+ // Apply to any new elements created after load
413
+ document.createElement = function createElement(nodeName) {
414
+ var element = nativeCreateElement(String(nodeName).toLowerCase());
415
+ return shiv(element);
416
+ };
417
+
418
+ // remove sandboxed iframe
419
+ document.removeChild(vbody);
420
+ }());
421
+
422
+ })
423
+ .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
424
+
425
+ (function(undefined) {
426
+
427
+ // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/detect.js
428
+ var detect = (function(){
429
+ if (!document.documentElement.dataset) {
430
+ return false;
431
+ }
432
+ var el = document.createElement('div');
433
+ el.setAttribute("data-a-b", "c");
434
+ return el.dataset && el.dataset.aB == "c";
435
+ }());
670
436
 
671
437
  if (detect) return
672
438
 
673
- // Polyfill from https://raw.githubusercontent.com/Financial-Times/polyfill-service/master/packages/polyfill-library/polyfills/DOMTokenList/polyfill.js
674
- (function (global) {
675
- var nativeImpl = "DOMTokenList" in global && global.DOMTokenList;
676
-
677
- if (
678
- !nativeImpl ||
679
- (
680
- !!document.createElementNS &&
681
- !!document.createElementNS('http://www.w3.org/2000/svg', 'svg') &&
682
- !(document.createElementNS("http://www.w3.org/2000/svg", "svg").classList instanceof DOMTokenList)
683
- )
684
- ) {
685
- global.DOMTokenList = (function() { // eslint-disable-line no-unused-vars
686
- var dpSupport = true;
687
- var defineGetter = function (object, name, fn, configurable) {
688
- if (Object.defineProperty)
689
- Object.defineProperty(object, name, {
690
- configurable: false === dpSupport ? true : !!configurable,
691
- get: fn
439
+ // Polyfill derived from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/polyfill.js
440
+ Object.defineProperty(Element.prototype, 'dataset', {
441
+ get: function() {
442
+ var element = this;
443
+ var attributes = this.attributes;
444
+ var map = {};
445
+
446
+ for (var i = 0; i < attributes.length; i++) {
447
+ var attribute = attributes[i];
448
+
449
+ // This regex has been edited from the original polyfill, to add
450
+ // support for period (.) separators in data-* attribute names. These
451
+ // are allowed in the HTML spec, but were not covered by the original
452
+ // polyfill's regex. We use periods in our i18n implementation.
453
+ if (attribute && attribute.name && (/^data-\w[.\w-]*$/).test(attribute.name)) {
454
+ var name = attribute.name;
455
+ var value = attribute.value;
456
+
457
+ var propName = name.substr(5).replace(/-./g, function (prop) {
458
+ return prop.charAt(1).toUpperCase();
459
+ });
460
+
461
+ // If this browser supports __defineGetter__ and __defineSetter__,
462
+ // continue using defineProperty. If not (like IE 8 and below), we use
463
+ // a hacky fallback which at least gives an object in the right format
464
+ if ('__defineGetter__' in Object.prototype && '__defineSetter__' in Object.prototype) {
465
+ Object.defineProperty(map, propName, {
466
+ enumerable: true,
467
+ get: function() {
468
+ return this.value;
469
+ }.bind({value: value || ''}),
470
+ set: function setter(name, value) {
471
+ if (typeof value !== 'undefined') {
472
+ this.setAttribute(name, value);
473
+ } else {
474
+ this.removeAttribute(name);
475
+ }
476
+ }.bind(element, name)
692
477
  });
478
+ } else {
479
+ map[propName] = value;
480
+ }
693
481
 
694
- else object.__defineGetter__(name, fn);
695
- };
696
-
697
- /** Ensure the browser allows Object.defineProperty to be used on native JavaScript objects. */
698
- try {
699
- defineGetter({}, "support");
700
- }
701
- catch (e) {
702
- dpSupport = false;
703
482
  }
483
+ }
484
+
485
+ return map;
486
+ }
487
+ });
704
488
 
489
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
705
490
 
706
- var _DOMTokenList = function (el, prop) {
707
- var that = this;
708
- var tokens = [];
709
- var tokenMap = {};
710
- var length = 0;
711
- var maxLength = 0;
712
- var addIndexGetter = function (i) {
713
- defineGetter(that, i, function () {
714
- preop();
715
- return tokens[i];
716
- }, false);
491
+ (function(undefined) {
717
492
 
718
- };
719
- var reindex = function () {
493
+ // Detection from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
494
+ var detect = ('trim' in String.prototype);
495
+
496
+ if (detect) return
720
497
 
721
- /** Define getter functions for array-like access to the tokenList's contents. */
722
- if (length >= maxLength)
723
- for (; maxLength < length; ++maxLength) {
724
- addIndexGetter(maxLength);
725
- }
726
- };
498
+ // Polyfill from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
499
+ String.prototype.trim = function () {
500
+ return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
501
+ };
727
502
 
728
- /** Helper function called at the start of each class method. Internal use only. */
729
- var preop = function () {
730
- var error;
731
- var i;
732
- var args = arguments;
733
- var rSpace = /\s+/;
734
-
735
- /** Validate the token/s passed to an instance method, if any. */
736
- if (args.length)
737
- for (i = 0; i < args.length; ++i)
738
- if (rSpace.test(args[i])) {
739
- error = new SyntaxError('String "' + args[i] + '" ' + "contains" + ' an invalid character');
740
- error.code = 5;
741
- error.name = "InvalidCharacterError";
742
- throw error;
743
- }
503
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
504
+
505
+ /* eslint-disable es-x/no-string-prototype-trim -- Polyfill imported */
506
+
507
+ /**
508
+ * Normalise string
509
+ *
510
+ * 'If it looks like a duck, and it quacks like a duck…' 🦆
511
+ *
512
+ * If the passed value looks like a boolean or a number, convert it to a boolean
513
+ * or number.
514
+ *
515
+ * Designed to be used to convert config passed via data attributes (which are
516
+ * always strings) into something sensible.
517
+ *
518
+ * @param {string} value - The value to normalise
519
+ * @returns {string | boolean | number | undefined} Normalised data
520
+ */
521
+ function normaliseString (value) {
522
+ if (typeof value !== 'string') {
523
+ return value
524
+ }
525
+
526
+ var trimmedValue = value.trim();
527
+
528
+ if (trimmedValue === 'true') {
529
+ return true
530
+ }
531
+
532
+ if (trimmedValue === 'false') {
533
+ return false
534
+ }
535
+
536
+ // Empty / whitespace-only strings are considered finite so we need to check
537
+ // the length of the trimmed string as well
538
+ if (trimmedValue.length > 0 && isFinite(trimmedValue)) {
539
+ return Number(trimmedValue)
540
+ }
541
+
542
+ return value
543
+ }
544
+
545
+ /**
546
+ * Normalise dataset
547
+ *
548
+ * Loop over an object and normalise each value using normaliseData function
549
+ *
550
+ * @param {DOMStringMap} dataset - HTML element dataset
551
+ * @returns {Object<string, unknown>} Normalised dataset
552
+ */
553
+ function normaliseDataset (dataset) {
554
+ var out = {};
555
+
556
+ for (var key in dataset) {
557
+ out[key] = normaliseString(dataset[key]);
558
+ }
559
+
560
+ return out
561
+ }
562
+
563
+ /**
564
+ * Internal support for selecting messages to render, with placeholder
565
+ * interpolation and locale-aware number formatting and pluralisation
566
+ *
567
+ * @class
568
+ * @private
569
+ * @param {Object<string, unknown>} translations - Key-value pairs of the translation strings to use.
570
+ * @param {object} [config] - Configuration options for the function.
571
+ * @param {string} [config.locale] - An overriding locale for the PluralRules functionality.
572
+ */
573
+ function I18n (translations, config) {
574
+ // Make list of translations available throughout function
575
+ this.translations = translations || {};
576
+
577
+ // The locale to use for PluralRules and NumberFormat
578
+ this.locale = (config && config.locale) || document.documentElement.lang || 'en';
579
+ }
580
+
581
+ /**
582
+ * The most used function - takes the key for a given piece of UI text and
583
+ * returns the appropriate string.
584
+ *
585
+ * @param {string} lookupKey - The lookup key of the string to use.
586
+ * @param {Object<string, unknown>} [options] - Any options passed with the translation string, e.g: for string interpolation.
587
+ * @returns {string} The appropriate translation string.
588
+ * @throws {Error} Lookup key required
589
+ * @throws {Error} Options required for `${}` placeholders
590
+ */
591
+ I18n.prototype.t = function (lookupKey, options) {
592
+ if (!lookupKey) {
593
+ // Print a console error if no lookup key has been provided
594
+ throw new Error('i18n: lookup key missing')
595
+ }
596
+
597
+ // If the `count` option is set, determine which plural suffix is needed and
598
+ // change the lookupKey to match. We check to see if it's undefined instead of
599
+ // falsy, as this could legitimately be 0.
600
+ if (options && typeof options.count !== 'undefined') {
601
+ // Get the plural suffix
602
+ lookupKey = lookupKey + '.' + this.getPluralSuffix(lookupKey, options.count);
603
+ }
604
+
605
+ if (lookupKey in this.translations) {
606
+ // Fetch the translation string for that lookup key
607
+ var translationString = this.translations[lookupKey];
744
608
 
609
+ // Check for ${} placeholders in the translation string
610
+ if (translationString.match(/%{(.\S+)}/)) {
611
+ if (!options) {
612
+ throw new Error('i18n: cannot replace placeholders in string if no option data provided')
613
+ }
745
614
 
746
- /** Split the new value apart by whitespace*/
747
- if (typeof el[prop] === "object") {
748
- tokens = ("" + el[prop].baseVal).replace(/^\s+|\s+$/g, "").split(rSpace);
749
- } else {
750
- tokens = ("" + el[prop]).replace(/^\s+|\s+$/g, "").split(rSpace);
751
- }
615
+ return this.replacePlaceholders(translationString, options)
616
+ } else {
617
+ return translationString
618
+ }
619
+ } else {
620
+ // If the key wasn't found in our translations object,
621
+ // return the lookup key itself as the fallback
622
+ return lookupKey
623
+ }
624
+ };
625
+
626
+ /**
627
+ * Takes a translation string with placeholders, and replaces the placeholders
628
+ * with the provided data
629
+ *
630
+ * @param {string} translationString - The translation string
631
+ * @param {Object<string, unknown>} options - Any options passed with the translation string, e.g: for string interpolation.
632
+ * @returns {string} The translation string to output, with ${} placeholders replaced
633
+ */
634
+ I18n.prototype.replacePlaceholders = function (translationString, options) {
635
+ var formatter;
636
+
637
+ if (this.hasIntlNumberFormatSupport()) {
638
+ formatter = new Intl.NumberFormat(this.locale);
639
+ }
640
+
641
+ return translationString.replace(/%{(.\S+)}/g, function (placeholderWithBraces, placeholderKey) {
642
+ if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
643
+ var placeholderValue = options[placeholderKey];
752
644
 
753
- /** Avoid treating blank strings as single-item token lists */
754
- if ("" === tokens[0]) tokens = [];
645
+ // If a user has passed `false` as the value for the placeholder
646
+ // treat it as though the value should not be displayed
647
+ if (placeholderValue === false) {
648
+ return ''
649
+ }
650
+
651
+ // If the placeholder's value is a number, localise the number formatting
652
+ if (typeof placeholderValue === 'number' && formatter) {
653
+ return formatter.format(placeholderValue)
654
+ }
655
+
656
+ return placeholderValue
657
+ } else {
658
+ throw new Error('i18n: no data found to replace ' + placeholderWithBraces + ' placeholder in string')
659
+ }
660
+ })
661
+ };
662
+
663
+ /**
664
+ * Check to see if the browser supports Intl and Intl.PluralRules.
665
+ *
666
+ * It requires all conditions to be met in order to be supported:
667
+ * - The browser supports the Intl class (true in IE11)
668
+ * - The implementation of Intl supports PluralRules (NOT true in IE11)
669
+ * - The browser/OS has plural rules for the current locale (browser dependent)
670
+ *
671
+ * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.
672
+ */
673
+ I18n.prototype.hasIntlPluralRulesSupport = function () {
674
+ return Boolean(window.Intl && ('PluralRules' in window.Intl && Intl.PluralRules.supportedLocalesOf(this.locale).length))
675
+ };
676
+
677
+ /**
678
+ * Check to see if the browser supports Intl and Intl.NumberFormat.
679
+ *
680
+ * It requires all conditions to be met in order to be supported:
681
+ * - The browser supports the Intl class (true in IE11)
682
+ * - The implementation of Intl supports NumberFormat (also true in IE11)
683
+ * - The browser/OS has number formatting rules for the current locale (browser dependent)
684
+ *
685
+ * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.
686
+ */
687
+ I18n.prototype.hasIntlNumberFormatSupport = function () {
688
+ return Boolean(window.Intl && ('NumberFormat' in window.Intl && Intl.NumberFormat.supportedLocalesOf(this.locale).length))
689
+ };
690
+
691
+ /**
692
+ * Get the appropriate suffix for the plural form.
693
+ *
694
+ * Uses Intl.PluralRules (or our own fallback implementation) to get the
695
+ * 'preferred' form to use for the given count.
696
+ *
697
+ * Checks that a translation has been provided for that plural form – if it
698
+ * hasn't, it'll fall back to the 'other' plural form (unless that doesn't exist
699
+ * either, in which case an error will be thrown)
700
+ *
701
+ * @param {string} lookupKey - The lookup key of the string to use.
702
+ * @param {number} count - Number used to determine which pluralisation to use.
703
+ * @returns {PluralRule} The suffix associated with the correct pluralisation for this locale.
704
+ * @throws {Error} Plural form `.other` required when preferred plural form is missing
705
+ */
706
+ I18n.prototype.getPluralSuffix = function (lookupKey, count) {
707
+ // Validate that the number is actually a number.
708
+ //
709
+ // Number(count) will turn anything that can't be converted to a Number type
710
+ // into 'NaN'. isFinite filters out NaN, as it isn't a finite number.
711
+ count = Number(count);
712
+ if (!isFinite(count)) { return 'other' }
713
+
714
+ var preferredForm;
715
+
716
+ // Check to verify that all the requirements for Intl.PluralRules are met.
717
+ // If so, we can use that instead of our custom implementation. Otherwise,
718
+ // use the hardcoded fallback.
719
+ if (this.hasIntlPluralRulesSupport()) {
720
+ preferredForm = new Intl.PluralRules(this.locale).select(count);
721
+ } else {
722
+ preferredForm = this.selectPluralFormUsingFallbackRules(count);
723
+ }
755
724
 
756
- /** Repopulate the internal token lists */
757
- tokenMap = {};
758
- for (i = 0; i < tokens.length; ++i)
759
- tokenMap[tokens[i]] = true;
760
- length = tokens.length;
761
- reindex();
725
+ // Use the correct plural form if provided
726
+ if (lookupKey + '.' + preferredForm in this.translations) {
727
+ return preferredForm
728
+ // Fall back to `other` if the plural form is missing, but log a warning
729
+ // to the console
730
+ } else if (lookupKey + '.other' in this.translations) {
731
+ if (console && 'warn' in console) {
732
+ console.warn('i18n: Missing plural form ".' + preferredForm + '" for "' +
733
+ this.locale + '" locale. Falling back to ".other".');
734
+ }
735
+
736
+ return 'other'
737
+ // If the required `other` plural form is missing, all we can do is error
738
+ } else {
739
+ throw new Error(
740
+ 'i18n: Plural form ".other" is required for "' + this.locale + '" locale'
741
+ )
742
+ }
743
+ };
744
+
745
+ /**
746
+ * Get the plural form using our fallback implementation
747
+ *
748
+ * This is split out into a separate function to make it easier to test the
749
+ * fallback behaviour in an environment where Intl.PluralRules exists.
750
+ *
751
+ * @param {number} count - Number used to determine which pluralisation to use.
752
+ * @returns {PluralRule} The pluralisation form for count in this locale.
753
+ */
754
+ I18n.prototype.selectPluralFormUsingFallbackRules = function (count) {
755
+ // Currently our custom code can only handle positive integers, so let's
756
+ // make sure our number is one of those.
757
+ count = Math.abs(Math.floor(count));
758
+
759
+ var ruleset = this.getPluralRulesForLocale();
760
+
761
+ if (ruleset) {
762
+ return I18n.pluralRules[ruleset](count)
763
+ }
764
+
765
+ return 'other'
766
+ };
767
+
768
+ /**
769
+ * Work out which pluralisation rules to use for the current locale
770
+ *
771
+ * The locale may include a regional indicator (such as en-GB), but we don't
772
+ * usually care about this part, as pluralisation rules are usually the same
773
+ * regardless of region. There are exceptions, however, (e.g. Portuguese) so
774
+ * this searches by both the full and shortened locale codes, just to be sure.
775
+ *
776
+ * @returns {PluralRuleName | undefined} The name of the pluralisation rule to use (a key for one
777
+ * of the functions in this.pluralRules)
778
+ */
779
+ I18n.prototype.getPluralRulesForLocale = function () {
780
+ var locale = this.locale;
781
+ var localeShort = locale.split('-')[0];
782
+
783
+ // Look through the plural rules map to find which `pluralRule` is
784
+ // appropriate for our current `locale`.
785
+ for (var pluralRule in I18n.pluralRulesMap) {
786
+ if (Object.prototype.hasOwnProperty.call(I18n.pluralRulesMap, pluralRule)) {
787
+ var languages = I18n.pluralRulesMap[pluralRule];
788
+ for (var i = 0; i < languages.length; i++) {
789
+ if (languages[i] === locale || languages[i] === localeShort) {
790
+ return pluralRule
791
+ }
792
+ }
793
+ }
794
+ }
795
+ };
796
+
797
+ /**
798
+ * Map of plural rules to languages where those rules apply.
799
+ *
800
+ * Note: These groups are named for the most dominant or recognisable language
801
+ * that uses each system. The groupings do not imply that the languages are
802
+ * related to one another. Many languages have evolved the same systems
803
+ * independently of one another.
804
+ *
805
+ * Code to support more languages can be found in the i18n spike:
806
+ * {@link https://github.com/alphagov/govuk-frontend/blob/spike-i18n-support/src/govuk/i18n.mjs}
807
+ *
808
+ * Languages currently supported:
809
+ *
810
+ * Arabic: Arabic (ar)
811
+ * Chinese: Burmese (my), Chinese (zh), Indonesian (id), Japanese (ja),
812
+ * Javanese (jv), Korean (ko), Malay (ms), Thai (th), Vietnamese (vi)
813
+ * French: Armenian (hy), Bangla (bn), French (fr), Gujarati (gu), Hindi (hi),
814
+ * Persian Farsi (fa), Punjabi (pa), Zulu (zu)
815
+ * German: Afrikaans (af), Albanian (sq), Azerbaijani (az), Basque (eu),
816
+ * Bulgarian (bg), Catalan (ca), Danish (da), Dutch (nl), English (en),
817
+ * Estonian (et), Finnish (fi), Georgian (ka), German (de), Greek (el),
818
+ * Hungarian (hu), Luxembourgish (lb), Norwegian (no), Somali (so),
819
+ * Swahili (sw), Swedish (sv), Tamil (ta), Telugu (te), Turkish (tr),
820
+ * Urdu (ur)
821
+ * Irish: Irish Gaelic (ga)
822
+ * Russian: Russian (ru), Ukrainian (uk)
823
+ * Scottish: Scottish Gaelic (gd)
824
+ * Spanish: European Portuguese (pt-PT), Italian (it), Spanish (es)
825
+ * Welsh: Welsh (cy)
826
+ *
827
+ * @type {Object<PluralRuleName, string[]>}
828
+ */
829
+ I18n.pluralRulesMap = {
830
+ arabic: ['ar'],
831
+ chinese: ['my', 'zh', 'id', 'ja', 'jv', 'ko', 'ms', 'th', 'vi'],
832
+ french: ['hy', 'bn', 'fr', 'gu', 'hi', 'fa', 'pa', 'zu'],
833
+ german: [
834
+ 'af', 'sq', 'az', 'eu', 'bg', 'ca', 'da', 'nl', 'en', 'et', 'fi', 'ka',
835
+ 'de', 'el', 'hu', 'lb', 'no', 'so', 'sw', 'sv', 'ta', 'te', 'tr', 'ur'
836
+ ],
837
+ irish: ['ga'],
838
+ russian: ['ru', 'uk'],
839
+ scottish: ['gd'],
840
+ spanish: ['pt-PT', 'it', 'es'],
841
+ welsh: ['cy']
842
+ };
843
+
844
+ /**
845
+ * Different pluralisation rule sets
846
+ *
847
+ * Returns the appropriate suffix for the plural form associated with `n`.
848
+ * Possible suffixes: 'zero', 'one', 'two', 'few', 'many', 'other' (the actual
849
+ * meaning of each differs per locale). 'other' should always exist, even in
850
+ * languages without plurals, such as Chinese.
851
+ * {@link https://cldr.unicode.org/index/cldr-spec/plural-rules}
852
+ *
853
+ * The count must be a positive integer. Negative numbers and decimals aren't accounted for
854
+ *
855
+ * @type {Object<string, function(number): PluralRule>}
856
+ */
857
+ I18n.pluralRules = {
858
+ /* eslint-disable jsdoc/require-jsdoc */
859
+ arabic: function (n) {
860
+ if (n === 0) { return 'zero' }
861
+ if (n === 1) { return 'one' }
862
+ if (n === 2) { return 'two' }
863
+ if (n % 100 >= 3 && n % 100 <= 10) { return 'few' }
864
+ if (n % 100 >= 11 && n % 100 <= 99) { return 'many' }
865
+ return 'other'
866
+ },
867
+ chinese: function () {
868
+ return 'other'
869
+ },
870
+ french: function (n) {
871
+ return n === 0 || n === 1 ? 'one' : 'other'
872
+ },
873
+ german: function (n) {
874
+ return n === 1 ? 'one' : 'other'
875
+ },
876
+ irish: function (n) {
877
+ if (n === 1) { return 'one' }
878
+ if (n === 2) { return 'two' }
879
+ if (n >= 3 && n <= 6) { return 'few' }
880
+ if (n >= 7 && n <= 10) { return 'many' }
881
+ return 'other'
882
+ },
883
+ russian: function (n) {
884
+ var lastTwo = n % 100;
885
+ var last = lastTwo % 10;
886
+ if (last === 1 && lastTwo !== 11) { return 'one' }
887
+ if (last >= 2 && last <= 4 && !(lastTwo >= 12 && lastTwo <= 14)) { return 'few' }
888
+ if (last === 0 || (last >= 5 && last <= 9) || (lastTwo >= 11 && lastTwo <= 14)) { return 'many' }
889
+ // Note: The 'other' suffix is only used by decimal numbers in Russian.
890
+ // We don't anticipate it being used, but it's here for consistency.
891
+ return 'other'
892
+ },
893
+ scottish: function (n) {
894
+ if (n === 1 || n === 11) { return 'one' }
895
+ if (n === 2 || n === 12) { return 'two' }
896
+ if ((n >= 3 && n <= 10) || (n >= 13 && n <= 19)) { return 'few' }
897
+ return 'other'
898
+ },
899
+ spanish: function (n) {
900
+ if (n === 1) { return 'one' }
901
+ if (n % 1000000 === 0 && n !== 0) { return 'many' }
902
+ return 'other'
903
+ },
904
+ welsh: function (n) {
905
+ if (n === 0) { return 'zero' }
906
+ if (n === 1) { return 'one' }
907
+ if (n === 2) { return 'two' }
908
+ if (n === 3) { return 'few' }
909
+ if (n === 6) { return 'many' }
910
+ return 'other'
911
+ }
912
+ /* eslint-enable jsdoc/require-jsdoc */
913
+ };
914
+
915
+ /**
916
+ * Supported languages for plural rules
917
+ *
918
+ * @typedef {'arabic' | 'chinese' | 'french' | 'german' | 'irish' | 'russian' | 'scottish' | 'spanish' | 'welsh'} PluralRuleName
919
+ */
920
+
921
+ /**
922
+ * Plural rule category mnemonic tags
923
+ *
924
+ * @typedef {'zero' | 'one' | 'two' | 'few' | 'many' | 'other'} PluralRule
925
+ */
926
+
927
+ /**
928
+ * Translated message by plural rule they correspond to.
929
+ *
930
+ * Allows to group pluralised messages under a single key when passing
931
+ * translations to a component's constructor
932
+ *
933
+ * @typedef {object} TranslationPluralForms
934
+ * @property {string} [other] - General plural form
935
+ * @property {string} [zero] - Plural form used with 0
936
+ * @property {string} [one] - Plural form used with 1
937
+ * @property {string} [two] - Plural form used with 2
938
+ * @property {string} [few] - Plural form used for a few
939
+ * @property {string} [many] - Plural form used for many
940
+ */
941
+
942
+ (function(undefined) {
943
+
944
+ // Detection from https://github.com/Financial-Times/polyfill-library/blob/v3.111.0/polyfills/Date/now/detect.js
945
+ var detect = ('Date' in self && 'now' in self.Date && 'getTime' in self.Date.prototype);
946
+
947
+ if (detect) return
948
+
949
+ // Polyfill from https://polyfill.io/v3/polyfill.js?version=3.111.0&features=Date.now&flags=always
950
+ Date.now = function () {
951
+ return new Date().getTime();
952
+ };
953
+
954
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
955
+
956
+ (function(undefined) {
957
+
958
+ // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/master/packages/polyfill-library/polyfills/DOMTokenList/detect.js
959
+ var detect = (
960
+ 'DOMTokenList' in this && (function (x) {
961
+ return 'classList' in x ? !x.classList.toggle('x', false) && !x.className : true;
962
+ })(document.createElement('x'))
963
+ );
964
+
965
+ if (detect) return
966
+
967
+ // Polyfill from https://raw.githubusercontent.com/Financial-Times/polyfill-service/master/packages/polyfill-library/polyfills/DOMTokenList/polyfill.js
968
+ (function (global) {
969
+ var nativeImpl = "DOMTokenList" in global && global.DOMTokenList;
970
+
971
+ if (
972
+ !nativeImpl ||
973
+ (
974
+ !!document.createElementNS &&
975
+ !!document.createElementNS('http://www.w3.org/2000/svg', 'svg') &&
976
+ !(document.createElementNS("http://www.w3.org/2000/svg", "svg").classList instanceof DOMTokenList)
977
+ )
978
+ ) {
979
+ global.DOMTokenList = (function() { // eslint-disable-line no-unused-vars
980
+ var dpSupport = true;
981
+ var defineGetter = function (object, name, fn, configurable) {
982
+ if (Object.defineProperty)
983
+ Object.defineProperty(object, name, {
984
+ configurable: false === dpSupport ? true : !!configurable,
985
+ get: fn
986
+ });
987
+
988
+ else object.__defineGetter__(name, fn);
762
989
  };
763
990
 
764
- /** Populate our internal token list if the targeted attribute of the subject element isn't empty. */
765
- preop();
991
+ /** Ensure the browser allows Object.defineProperty to be used on native JavaScript objects. */
992
+ try {
993
+ defineGetter({}, "support");
994
+ }
995
+ catch (e) {
996
+ dpSupport = false;
997
+ }
998
+
999
+
1000
+ var _DOMTokenList = function (el, prop) {
1001
+ var that = this;
1002
+ var tokens = [];
1003
+ var tokenMap = {};
1004
+ var length = 0;
1005
+ var maxLength = 0;
1006
+ var addIndexGetter = function (i) {
1007
+ defineGetter(that, i, function () {
1008
+ preop();
1009
+ return tokens[i];
1010
+ }, false);
1011
+
1012
+ };
1013
+ var reindex = function () {
1014
+
1015
+ /** Define getter functions for array-like access to the tokenList's contents. */
1016
+ if (length >= maxLength)
1017
+ for (; maxLength < length; ++maxLength) {
1018
+ addIndexGetter(maxLength);
1019
+ }
1020
+ };
1021
+
1022
+ /** Helper function called at the start of each class method. Internal use only. */
1023
+ var preop = function () {
1024
+ var error;
1025
+ var i;
1026
+ var args = arguments;
1027
+ var rSpace = /\s+/;
1028
+
1029
+ /** Validate the token/s passed to an instance method, if any. */
1030
+ if (args.length)
1031
+ for (i = 0; i < args.length; ++i)
1032
+ if (rSpace.test(args[i])) {
1033
+ error = new SyntaxError('String "' + args[i] + '" ' + "contains" + ' an invalid character');
1034
+ error.code = 5;
1035
+ error.name = "InvalidCharacterError";
1036
+ throw error;
1037
+ }
1038
+
1039
+
1040
+ /** Split the new value apart by whitespace*/
1041
+ if (typeof el[prop] === "object") {
1042
+ tokens = ("" + el[prop].baseVal).replace(/^\s+|\s+$/g, "").split(rSpace);
1043
+ } else {
1044
+ tokens = ("" + el[prop]).replace(/^\s+|\s+$/g, "").split(rSpace);
1045
+ }
1046
+
1047
+ /** Avoid treating blank strings as single-item token lists */
1048
+ if ("" === tokens[0]) tokens = [];
766
1049
 
767
- /** Return the number of tokens in the underlying string. Read-only. */
768
- defineGetter(that, "length", function () {
1050
+ /** Repopulate the internal token lists */
1051
+ tokenMap = {};
1052
+ for (i = 0; i < tokens.length; ++i)
1053
+ tokenMap[tokens[i]] = true;
1054
+ length = tokens.length;
1055
+ reindex();
1056
+ };
1057
+
1058
+ /** Populate our internal token list if the targeted attribute of the subject element isn't empty. */
769
1059
  preop();
770
- return length;
771
- });
772
1060
 
773
- /** Override the default toString/toLocaleString methods to return a space-delimited list of tokens when typecast. */
774
- that.toLocaleString =
775
- that.toString = function () {
1061
+ /** Return the number of tokens in the underlying string. Read-only. */
1062
+ defineGetter(that, "length", function () {
1063
+ preop();
1064
+ return length;
1065
+ });
1066
+
1067
+ /** Override the default toString/toLocaleString methods to return a space-delimited list of tokens when typecast. */
1068
+ that.toLocaleString =
1069
+ that.toString = function () {
1070
+ preop();
1071
+ return tokens.join(" ");
1072
+ };
1073
+
1074
+ that.item = function (idx) {
776
1075
  preop();
777
- return tokens.join(" ");
1076
+ return tokens[idx];
778
1077
  };
779
1078
 
780
- that.item = function (idx) {
781
- preop();
782
- return tokens[idx];
783
- };
1079
+ that.contains = function (token) {
1080
+ preop();
1081
+ return !!tokenMap[token];
1082
+ };
784
1083
 
785
- that.contains = function (token) {
786
- preop();
787
- return !!tokenMap[token];
788
- };
1084
+ that.add = function () {
1085
+ preop.apply(that, args = arguments);
789
1086
 
790
- that.add = function () {
791
- preop.apply(that, args = arguments);
1087
+ for (var args, token, i = 0, l = args.length; i < l; ++i) {
1088
+ token = args[i];
1089
+ if (!tokenMap[token]) {
1090
+ tokens.push(token);
1091
+ tokenMap[token] = true;
1092
+ }
1093
+ }
792
1094
 
793
- for (var args, token, i = 0, l = args.length; i < l; ++i) {
794
- token = args[i];
795
- if (!tokenMap[token]) {
796
- tokens.push(token);
797
- tokenMap[token] = true;
1095
+ /** Update the targeted attribute of the attached element if the token list's changed. */
1096
+ if (length !== tokens.length) {
1097
+ length = tokens.length >>> 0;
1098
+ if (typeof el[prop] === "object") {
1099
+ el[prop].baseVal = tokens.join(" ");
1100
+ } else {
1101
+ el[prop] = tokens.join(" ");
1102
+ }
1103
+ reindex();
798
1104
  }
799
- }
1105
+ };
1106
+
1107
+ that.remove = function () {
1108
+ preop.apply(that, args = arguments);
1109
+
1110
+ /** Build a hash of token names to compare against when recollecting our token list. */
1111
+ for (var args, ignore = {}, i = 0, t = []; i < args.length; ++i) {
1112
+ ignore[args[i]] = true;
1113
+ delete tokenMap[args[i]];
1114
+ }
1115
+
1116
+ /** Run through our tokens list and reassign only those that aren't defined in the hash declared above. */
1117
+ for (i = 0; i < tokens.length; ++i)
1118
+ if (!ignore[tokens[i]]) t.push(tokens[i]);
800
1119
 
801
- /** Update the targeted attribute of the attached element if the token list's changed. */
802
- if (length !== tokens.length) {
803
- length = tokens.length >>> 0;
1120
+ tokens = t;
1121
+ length = t.length >>> 0;
1122
+
1123
+ /** Update the targeted attribute of the attached element. */
804
1124
  if (typeof el[prop] === "object") {
805
1125
  el[prop].baseVal = tokens.join(" ");
806
1126
  } else {
807
1127
  el[prop] = tokens.join(" ");
808
1128
  }
809
1129
  reindex();
810
- }
811
- };
812
-
813
- that.remove = function () {
814
- preop.apply(that, args = arguments);
815
-
816
- /** Build a hash of token names to compare against when recollecting our token list. */
817
- for (var args, ignore = {}, i = 0, t = []; i < args.length; ++i) {
818
- ignore[args[i]] = true;
819
- delete tokenMap[args[i]];
820
- }
821
-
822
- /** Run through our tokens list and reassign only those that aren't defined in the hash declared above. */
823
- for (i = 0; i < tokens.length; ++i)
824
- if (!ignore[tokens[i]]) t.push(tokens[i]);
825
-
826
- tokens = t;
827
- length = t.length >>> 0;
828
-
829
- /** Update the targeted attribute of the attached element. */
830
- if (typeof el[prop] === "object") {
831
- el[prop].baseVal = tokens.join(" ");
832
- } else {
833
- el[prop] = tokens.join(" ");
834
- }
835
- reindex();
836
- };
1130
+ };
837
1131
 
838
- that.toggle = function (token, force) {
839
- preop.apply(that, [token]);
1132
+ that.toggle = function (token, force) {
1133
+ preop.apply(that, [token]);
1134
+
1135
+ /** Token state's being forced. */
1136
+ if (undefined !== force) {
1137
+ if (force) {
1138
+ that.add(token);
1139
+ return true;
1140
+ } else {
1141
+ that.remove(token);
1142
+ return false;
1143
+ }
1144
+ }
840
1145
 
841
- /** Token state's being forced. */
842
- if (undefined !== force) {
843
- if (force) {
844
- that.add(token);
845
- return true;
846
- } else {
1146
+ /** Token already exists in tokenList. Remove it, and return FALSE. */
1147
+ if (tokenMap[token]) {
847
1148
  that.remove(token);
848
1149
  return false;
849
1150
  }
850
- }
851
1151
 
852
- /** Token already exists in tokenList. Remove it, and return FALSE. */
853
- if (tokenMap[token]) {
854
- that.remove(token);
855
- return false;
856
- }
1152
+ /** Otherwise, add the token and return TRUE. */
1153
+ that.add(token);
1154
+ return true;
1155
+ };
857
1156
 
858
- /** Otherwise, add the token and return TRUE. */
859
- that.add(token);
860
- return true;
1157
+ return that;
861
1158
  };
862
1159
 
863
- return that;
1160
+ return _DOMTokenList;
1161
+ }());
1162
+ }
1163
+
1164
+ // Add second argument to native DOMTokenList.toggle() if necessary
1165
+ (function () {
1166
+ var e = document.createElement('span');
1167
+ if (!('classList' in e)) return;
1168
+ e.classList.toggle('x', false);
1169
+ if (!e.classList.contains('x')) return;
1170
+ e.classList.constructor.prototype.toggle = function toggle(token /*, force*/) {
1171
+ var force = arguments[1];
1172
+ if (force === undefined) {
1173
+ var add = !this.contains(token);
1174
+ this[add ? 'add' : 'remove'](token);
1175
+ return add;
1176
+ }
1177
+ force = !!force;
1178
+ this[force ? 'add' : 'remove'](token);
1179
+ return force;
864
1180
  };
1181
+ }());
865
1182
 
866
- return _DOMTokenList;
1183
+ // Add multiple arguments to native DOMTokenList.add() if necessary
1184
+ (function () {
1185
+ var e = document.createElement('span');
1186
+ if (!('classList' in e)) return;
1187
+ e.classList.add('a', 'b');
1188
+ if (e.classList.contains('b')) return;
1189
+ var native = e.classList.constructor.prototype.add;
1190
+ e.classList.constructor.prototype.add = function () {
1191
+ var args = arguments;
1192
+ var l = arguments.length;
1193
+ for (var i = 0; i < l; i++) {
1194
+ native.call(this, args[i]);
1195
+ }
1196
+ };
867
1197
  }());
868
- }
869
1198
 
870
- // Add second argument to native DOMTokenList.toggle() if necessary
871
- (function () {
872
- var e = document.createElement('span');
873
- if (!('classList' in e)) return;
874
- e.classList.toggle('x', false);
875
- if (!e.classList.contains('x')) return;
876
- e.classList.constructor.prototype.toggle = function toggle(token /*, force*/) {
877
- var force = arguments[1];
878
- if (force === undefined) {
879
- var add = !this.contains(token);
880
- this[add ? 'add' : 'remove'](token);
881
- return add;
882
- }
883
- force = !!force;
884
- this[force ? 'add' : 'remove'](token);
885
- return force;
886
- };
887
- }());
888
-
889
- // Add multiple arguments to native DOMTokenList.add() if necessary
890
- (function () {
891
- var e = document.createElement('span');
892
- if (!('classList' in e)) return;
893
- e.classList.add('a', 'b');
894
- if (e.classList.contains('b')) return;
895
- var native = e.classList.constructor.prototype.add;
896
- e.classList.constructor.prototype.add = function () {
897
- var args = arguments;
898
- var l = arguments.length;
899
- for (var i = 0; i < l; i++) {
900
- native.call(this, args[i]);
901
- }
1199
+ // Add multiple arguments to native DOMTokenList.remove() if necessary
1200
+ (function () {
1201
+ var e = document.createElement('span');
1202
+ if (!('classList' in e)) return;
1203
+ e.classList.add('a');
1204
+ e.classList.add('b');
1205
+ e.classList.remove('a', 'b');
1206
+ if (!e.classList.contains('b')) return;
1207
+ var native = e.classList.constructor.prototype.remove;
1208
+ e.classList.constructor.prototype.remove = function () {
1209
+ var args = arguments;
1210
+ var l = arguments.length;
1211
+ for (var i = 0; i < l; i++) {
1212
+ native.call(this, args[i]);
1213
+ }
1214
+ };
1215
+ }());
1216
+
1217
+ }(this));
1218
+
1219
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1220
+
1221
+ (function(undefined) {
1222
+
1223
+ // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/8717a9e04ac7aff99b4980fbedead98036b0929a/packages/polyfill-library/polyfills/Element/prototype/classList/detect.js
1224
+ var detect = (
1225
+ 'document' in this && "classList" in document.documentElement && 'Element' in this && 'classList' in Element.prototype && (function () {
1226
+ var e = document.createElement('span');
1227
+ e.classList.add('a', 'b');
1228
+ return e.classList.contains('b');
1229
+ }())
1230
+ );
1231
+
1232
+ if (detect) return
1233
+
1234
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Element.prototype.classList&flags=always
1235
+ (function (global) {
1236
+ var dpSupport = true;
1237
+ var defineGetter = function (object, name, fn, configurable) {
1238
+ if (Object.defineProperty)
1239
+ Object.defineProperty(object, name, {
1240
+ configurable: false === dpSupport ? true : !!configurable,
1241
+ get: fn
1242
+ });
1243
+
1244
+ else object.__defineGetter__(name, fn);
902
1245
  };
903
- }());
904
-
905
- // Add multiple arguments to native DOMTokenList.remove() if necessary
906
- (function () {
907
- var e = document.createElement('span');
908
- if (!('classList' in e)) return;
909
- e.classList.add('a');
910
- e.classList.add('b');
911
- e.classList.remove('a', 'b');
912
- if (!e.classList.contains('b')) return;
913
- var native = e.classList.constructor.prototype.remove;
914
- e.classList.constructor.prototype.remove = function () {
915
- var args = arguments;
916
- var l = arguments.length;
917
- for (var i = 0; i < l; i++) {
918
- native.call(this, args[i]);
919
- }
1246
+ /** Ensure the browser allows Object.defineProperty to be used on native JavaScript objects. */
1247
+ try {
1248
+ defineGetter({}, "support");
1249
+ }
1250
+ catch (e) {
1251
+ dpSupport = false;
1252
+ }
1253
+ /** Polyfills a property with a DOMTokenList */
1254
+ var addProp = function (o, name, attr) {
1255
+
1256
+ defineGetter(o.prototype, name, function () {
1257
+ var tokenList;
1258
+
1259
+ var THIS = this,
1260
+
1261
+ /** Prevent this from firing twice for some reason. What the hell, IE. */
1262
+ gibberishProperty = "__defineGetter__" + "DEFINE_PROPERTY" + name;
1263
+ if(THIS[gibberishProperty]) return tokenList;
1264
+ THIS[gibberishProperty] = true;
1265
+
1266
+ /**
1267
+ * IE8 can't define properties on native JavaScript objects, so we'll use a dumb hack instead.
1268
+ *
1269
+ * What this is doing is creating a dummy element ("reflection") inside a detached phantom node ("mirror")
1270
+ * that serves as the target of Object.defineProperty instead. While we could simply use the subject HTML
1271
+ * element instead, this would conflict with element types which use indexed properties (such as forms and
1272
+ * select lists).
1273
+ */
1274
+ if (false === dpSupport) {
1275
+
1276
+ var visage;
1277
+ var mirror = addProp.mirror || document.createElement("div");
1278
+ var reflections = mirror.childNodes;
1279
+ var l = reflections.length;
1280
+
1281
+ for (var i = 0; i < l; ++i)
1282
+ if (reflections[i]._R === THIS) {
1283
+ visage = reflections[i];
1284
+ break;
1285
+ }
1286
+
1287
+ /** Couldn't find an element's reflection inside the mirror. Materialise one. */
1288
+ visage || (visage = mirror.appendChild(document.createElement("div")));
1289
+
1290
+ tokenList = DOMTokenList.call(visage, THIS, attr);
1291
+ } else tokenList = new DOMTokenList(THIS, attr);
1292
+
1293
+ defineGetter(THIS, name, function () {
1294
+ return tokenList;
1295
+ });
1296
+ delete THIS[gibberishProperty];
1297
+
1298
+ return tokenList;
1299
+ }, true);
920
1300
  };
921
- }());
922
1301
 
923
- }(this));
1302
+ addProp(global.Element, "classList", "className");
1303
+ addProp(global.HTMLElement, "classList", "className");
1304
+ addProp(global.HTMLLinkElement, "relList", "rel");
1305
+ addProp(global.HTMLAnchorElement, "relList", "rel");
1306
+ addProp(global.HTMLAreaElement, "relList", "rel");
1307
+ }(this));
924
1308
 
925
- }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1309
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
926
1310
 
927
- (function(undefined) {
1311
+ (function(undefined) {
928
1312
 
929
- // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/8717a9e04ac7aff99b4980fbedead98036b0929a/packages/polyfill-library/polyfills/Element/prototype/classList/detect.js
930
- var detect = (
931
- 'document' in this && "classList" in document.documentElement && 'Element' in this && 'classList' in Element.prototype && (function () {
932
- var e = document.createElement('span');
933
- e.classList.add('a', 'b');
934
- return e.classList.contains('b');
935
- }())
936
- );
1313
+ // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Window/detect.js
1314
+ var detect = ('Window' in this);
1315
+
1316
+ if (detect) return
1317
+
1318
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Window&flags=always
1319
+ if ((typeof WorkerGlobalScope === "undefined") && (typeof importScripts !== "function")) {
1320
+ (function (global) {
1321
+ if (global.constructor) {
1322
+ global.Window = global.constructor;
1323
+ } else {
1324
+ (global.Window = global.constructor = new Function('return function Window() {}')()).prototype = this;
1325
+ }
1326
+ }(this));
1327
+ }
1328
+
1329
+ })
1330
+ .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1331
+
1332
+ (function(undefined) {
1333
+
1334
+ // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Event/detect.js
1335
+ var detect = (
1336
+ (function(global) {
1337
+
1338
+ if (!('Event' in global)) return false;
1339
+ if (typeof global.Event === 'function') return true;
1340
+
1341
+ try {
1342
+
1343
+ // In IE 9-11, the Event object exists but cannot be instantiated
1344
+ new Event('click');
1345
+ return true;
1346
+ } catch(e) {
1347
+ return false;
1348
+ }
1349
+ }(this))
1350
+ );
1351
+
1352
+ if (detect) return
1353
+
1354
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Event&flags=always
1355
+ (function () {
1356
+ var unlistenableWindowEvents = {
1357
+ click: 1,
1358
+ dblclick: 1,
1359
+ keyup: 1,
1360
+ keypress: 1,
1361
+ keydown: 1,
1362
+ mousedown: 1,
1363
+ mouseup: 1,
1364
+ mousemove: 1,
1365
+ mouseover: 1,
1366
+ mouseenter: 1,
1367
+ mouseleave: 1,
1368
+ mouseout: 1,
1369
+ storage: 1,
1370
+ storagecommit: 1,
1371
+ textinput: 1
1372
+ };
1373
+
1374
+ // This polyfill depends on availability of `document` so will not run in a worker
1375
+ // However, we asssume there are no browsers with worker support that lack proper
1376
+ // support for `Event` within the worker
1377
+ if (typeof document === 'undefined' || typeof window === 'undefined') return;
1378
+
1379
+ function indexOf(array, element) {
1380
+ var
1381
+ index = -1,
1382
+ length = array.length;
1383
+
1384
+ while (++index < length) {
1385
+ if (index in array && array[index] === element) {
1386
+ return index;
1387
+ }
1388
+ }
1389
+
1390
+ return -1;
1391
+ }
1392
+
1393
+ var existingProto = (window.Event && window.Event.prototype) || null;
1394
+ window.Event = Window.prototype.Event = function Event(type, eventInitDict) {
1395
+ if (!type) {
1396
+ throw new Error('Not enough arguments');
1397
+ }
1398
+
1399
+ var event;
1400
+ // Shortcut if browser supports createEvent
1401
+ if ('createEvent' in document) {
1402
+ event = document.createEvent('Event');
1403
+ var bubbles = eventInitDict && eventInitDict.bubbles !== undefined ? eventInitDict.bubbles : false;
1404
+ var cancelable = eventInitDict && eventInitDict.cancelable !== undefined ? eventInitDict.cancelable : false;
1405
+
1406
+ event.initEvent(type, bubbles, cancelable);
1407
+
1408
+ return event;
1409
+ }
1410
+
1411
+ event = document.createEventObject();
1412
+
1413
+ event.type = type;
1414
+ event.bubbles = eventInitDict && eventInitDict.bubbles !== undefined ? eventInitDict.bubbles : false;
1415
+ event.cancelable = eventInitDict && eventInitDict.cancelable !== undefined ? eventInitDict.cancelable : false;
1416
+
1417
+ return event;
1418
+ };
1419
+ if (existingProto) {
1420
+ Object.defineProperty(window.Event, 'prototype', {
1421
+ configurable: false,
1422
+ enumerable: false,
1423
+ writable: true,
1424
+ value: existingProto
1425
+ });
1426
+ }
1427
+
1428
+ if (!('createEvent' in document)) {
1429
+ window.addEventListener = Window.prototype.addEventListener = Document.prototype.addEventListener = Element.prototype.addEventListener = function addEventListener() {
1430
+ var
1431
+ element = this,
1432
+ type = arguments[0],
1433
+ listener = arguments[1];
1434
+
1435
+ if (element === window && type in unlistenableWindowEvents) {
1436
+ throw new Error('In IE8 the event: ' + type + ' is not available on the window object. Please see https://github.com/Financial-Times/polyfill-service/issues/317 for more information.');
1437
+ }
1438
+
1439
+ if (!element._events) {
1440
+ element._events = {};
1441
+ }
1442
+
1443
+ if (!element._events[type]) {
1444
+ element._events[type] = function (event) {
1445
+ var
1446
+ list = element._events[event.type].list,
1447
+ events = list.slice(),
1448
+ index = -1,
1449
+ length = events.length,
1450
+ eventElement;
1451
+
1452
+ event.preventDefault = function preventDefault() {
1453
+ if (event.cancelable !== false) {
1454
+ event.returnValue = false;
1455
+ }
1456
+ };
1457
+
1458
+ event.stopPropagation = function stopPropagation() {
1459
+ event.cancelBubble = true;
1460
+ };
1461
+
1462
+ event.stopImmediatePropagation = function stopImmediatePropagation() {
1463
+ event.cancelBubble = true;
1464
+ event.cancelImmediate = true;
1465
+ };
1466
+
1467
+ event.currentTarget = element;
1468
+ event.relatedTarget = event.fromElement || null;
1469
+ event.target = event.target || event.srcElement || element;
1470
+ event.timeStamp = new Date().getTime();
1471
+
1472
+ if (event.clientX) {
1473
+ event.pageX = event.clientX + document.documentElement.scrollLeft;
1474
+ event.pageY = event.clientY + document.documentElement.scrollTop;
1475
+ }
1476
+
1477
+ while (++index < length && !event.cancelImmediate) {
1478
+ if (index in events) {
1479
+ eventElement = events[index];
1480
+
1481
+ if (indexOf(list, eventElement) !== -1 && typeof eventElement === 'function') {
1482
+ eventElement.call(element, event);
1483
+ }
1484
+ }
1485
+ }
1486
+ };
1487
+
1488
+ element._events[type].list = [];
1489
+
1490
+ if (element.attachEvent) {
1491
+ element.attachEvent('on' + type, element._events[type]);
1492
+ }
1493
+ }
1494
+
1495
+ element._events[type].list.push(listener);
1496
+ };
1497
+
1498
+ window.removeEventListener = Window.prototype.removeEventListener = Document.prototype.removeEventListener = Element.prototype.removeEventListener = function removeEventListener() {
1499
+ var
1500
+ element = this,
1501
+ type = arguments[0],
1502
+ listener = arguments[1],
1503
+ index;
1504
+
1505
+ if (element._events && element._events[type] && element._events[type].list) {
1506
+ index = indexOf(element._events[type].list, listener);
1507
+
1508
+ if (index !== -1) {
1509
+ element._events[type].list.splice(index, 1);
1510
+
1511
+ if (!element._events[type].list.length) {
1512
+ if (element.detachEvent) {
1513
+ element.detachEvent('on' + type, element._events[type]);
1514
+ }
1515
+ delete element._events[type];
1516
+ }
1517
+ }
1518
+ }
1519
+ };
1520
+
1521
+ window.dispatchEvent = Window.prototype.dispatchEvent = Document.prototype.dispatchEvent = Element.prototype.dispatchEvent = function dispatchEvent(event) {
1522
+ if (!arguments.length) {
1523
+ throw new Error('Not enough arguments');
1524
+ }
1525
+
1526
+ if (!event || typeof event.type !== 'string') {
1527
+ throw new Error('DOM Events Exception 0');
1528
+ }
1529
+
1530
+ var element = this, type = event.type;
1531
+
1532
+ try {
1533
+ if (!event.bubbles) {
1534
+ event.cancelBubble = true;
1535
+
1536
+ var cancelBubbleEvent = function (event) {
1537
+ event.cancelBubble = true;
1538
+
1539
+ (element || window).detachEvent('on' + type, cancelBubbleEvent);
1540
+ };
1541
+
1542
+ this.attachEvent('on' + type, cancelBubbleEvent);
1543
+ }
1544
+
1545
+ this.fireEvent('on' + type, event);
1546
+ } catch (error) {
1547
+ event.target = element;
1548
+
1549
+ do {
1550
+ event.currentTarget = element;
1551
+
1552
+ if ('_events' in element && typeof element._events[type] === 'function') {
1553
+ element._events[type].call(element, event);
1554
+ }
1555
+
1556
+ if (typeof element['on' + type] === 'function') {
1557
+ element['on' + type].call(element, event);
1558
+ }
1559
+
1560
+ element = element.nodeType === 9 ? element.parentWindow : element.parentNode;
1561
+ } while (element && !event.cancelBubble);
1562
+ }
1563
+
1564
+ return true;
1565
+ };
1566
+
1567
+ // Add the DOMContentLoaded Event
1568
+ document.attachEvent('onreadystatechange', function() {
1569
+ if (document.readyState === 'complete') {
1570
+ document.dispatchEvent(new Event('DOMContentLoaded', {
1571
+ bubbles: true
1572
+ }));
1573
+ }
1574
+ });
1575
+ }
1576
+ }());
1577
+
1578
+ })
1579
+ .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1580
+
1581
+ (function(undefined) {
1582
+ // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Function/prototype/bind/detect.js
1583
+ var detect = 'bind' in Function.prototype;
937
1584
 
938
1585
  if (detect) return
939
1586
 
940
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Element.prototype.classList&flags=always
941
- (function (global) {
942
- var dpSupport = true;
943
- var defineGetter = function (object, name, fn, configurable) {
944
- if (Object.defineProperty)
945
- Object.defineProperty(object, name, {
946
- configurable: false === dpSupport ? true : !!configurable,
947
- get: fn
948
- });
949
-
950
- else object.__defineGetter__(name, fn);
951
- };
952
- /** Ensure the browser allows Object.defineProperty to be used on native JavaScript objects. */
953
- try {
954
- defineGetter({}, "support");
955
- }
956
- catch (e) {
957
- dpSupport = false;
958
- }
959
- /** Polyfills a property with a DOMTokenList */
960
- var addProp = function (o, name, attr) {
961
-
962
- defineGetter(o.prototype, name, function () {
963
- var tokenList;
964
-
965
- var THIS = this,
966
-
967
- /** Prevent this from firing twice for some reason. What the hell, IE. */
968
- gibberishProperty = "__defineGetter__" + "DEFINE_PROPERTY" + name;
969
- if(THIS[gibberishProperty]) return tokenList;
970
- THIS[gibberishProperty] = true;
971
-
972
- /**
973
- * IE8 can't define properties on native JavaScript objects, so we'll use a dumb hack instead.
974
- *
975
- * What this is doing is creating a dummy element ("reflection") inside a detached phantom node ("mirror")
976
- * that serves as the target of Object.defineProperty instead. While we could simply use the subject HTML
977
- * element instead, this would conflict with element types which use indexed properties (such as forms and
978
- * select lists).
979
- */
980
- if (false === dpSupport) {
981
-
982
- var visage;
983
- var mirror = addProp.mirror || document.createElement("div");
984
- var reflections = mirror.childNodes;
985
- var l = reflections.length;
986
-
987
- for (var i = 0; i < l; ++i)
988
- if (reflections[i]._R === THIS) {
989
- visage = reflections[i];
990
- break;
991
- }
992
-
993
- /** Couldn't find an element's reflection inside the mirror. Materialise one. */
994
- visage || (visage = mirror.appendChild(document.createElement("div")));
995
-
996
- tokenList = DOMTokenList.call(visage, THIS, attr);
997
- } else tokenList = new DOMTokenList(THIS, attr);
998
-
999
- defineGetter(THIS, name, function () {
1000
- return tokenList;
1001
- });
1002
- delete THIS[gibberishProperty];
1587
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Function.prototype.bind&flags=always
1588
+ Object.defineProperty(Function.prototype, 'bind', {
1589
+ value: function bind(that) { // .length is 1
1590
+ // add necessary es5-shim utilities
1591
+ var $Array = Array;
1592
+ var $Object = Object;
1593
+ var ObjectPrototype = $Object.prototype;
1594
+ var ArrayPrototype = $Array.prototype;
1595
+ var Empty = function Empty() {};
1596
+ var to_string = ObjectPrototype.toString;
1597
+ var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
1598
+ var isCallable; /* inlined from https://npmjs.com/is-callable */ var fnToStr = Function.prototype.toString, tryFunctionObject = function tryFunctionObject(value) { try { fnToStr.call(value); return true; } catch (e) { return false; } }, fnClass = '[object Function]', genClass = '[object GeneratorFunction]'; isCallable = function isCallable(value) { if (typeof value !== 'function') { return false; } if (hasToStringTag) { return tryFunctionObject(value); } var strClass = to_string.call(value); return strClass === fnClass || strClass === genClass; };
1599
+ var array_slice = ArrayPrototype.slice;
1600
+ var array_concat = ArrayPrototype.concat;
1601
+ var array_push = ArrayPrototype.push;
1602
+ var max = Math.max;
1603
+ // /add necessary es5-shim utilities
1604
+
1605
+ // 1. Let Target be the this value.
1606
+ var target = this;
1607
+ // 2. If IsCallable(Target) is false, throw a TypeError exception.
1608
+ if (!isCallable(target)) {
1609
+ throw new TypeError('Function.prototype.bind called on incompatible ' + target);
1610
+ }
1611
+ // 3. Let A be a new (possibly empty) internal list of all of the
1612
+ // argument values provided after thisArg (arg1, arg2 etc), in order.
1613
+ // XXX slicedArgs will stand in for "A" if used
1614
+ var args = array_slice.call(arguments, 1); // for normal call
1615
+ // 4. Let F be a new native ECMAScript object.
1616
+ // 11. Set the [[Prototype]] internal property of F to the standard
1617
+ // built-in Function prototype object as specified in 15.3.3.1.
1618
+ // 12. Set the [[Call]] internal property of F as described in
1619
+ // 15.3.4.5.1.
1620
+ // 13. Set the [[Construct]] internal property of F as described in
1621
+ // 15.3.4.5.2.
1622
+ // 14. Set the [[HasInstance]] internal property of F as described in
1623
+ // 15.3.4.5.3.
1624
+ var bound;
1625
+ var binder = function () {
1626
+
1627
+ if (this instanceof bound) {
1628
+ // 15.3.4.5.2 [[Construct]]
1629
+ // When the [[Construct]] internal method of a function object,
1630
+ // F that was created using the bind function is called with a
1631
+ // list of arguments ExtraArgs, the following steps are taken:
1632
+ // 1. Let target be the value of F's [[TargetFunction]]
1633
+ // internal property.
1634
+ // 2. If target has no [[Construct]] internal method, a
1635
+ // TypeError exception is thrown.
1636
+ // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
1637
+ // property.
1638
+ // 4. Let args be a new list containing the same values as the
1639
+ // list boundArgs in the same order followed by the same
1640
+ // values as the list ExtraArgs in the same order.
1641
+ // 5. Return the result of calling the [[Construct]] internal
1642
+ // method of target providing args as the arguments.
1643
+
1644
+ var result = target.apply(
1645
+ this,
1646
+ array_concat.call(args, array_slice.call(arguments))
1647
+ );
1648
+ if ($Object(result) === result) {
1649
+ return result;
1650
+ }
1651
+ return this;
1003
1652
 
1004
- return tokenList;
1005
- }, true);
1006
- };
1653
+ } else {
1654
+ // 15.3.4.5.1 [[Call]]
1655
+ // When the [[Call]] internal method of a function object, F,
1656
+ // which was created using the bind function is called with a
1657
+ // this value and a list of arguments ExtraArgs, the following
1658
+ // steps are taken:
1659
+ // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
1660
+ // property.
1661
+ // 2. Let boundThis be the value of F's [[BoundThis]] internal
1662
+ // property.
1663
+ // 3. Let target be the value of F's [[TargetFunction]] internal
1664
+ // property.
1665
+ // 4. Let args be a new list containing the same values as the
1666
+ // list boundArgs in the same order followed by the same
1667
+ // values as the list ExtraArgs in the same order.
1668
+ // 5. Return the result of calling the [[Call]] internal method
1669
+ // of target providing boundThis as the this value and
1670
+ // providing args as the arguments.
1671
+
1672
+ // equiv: target.call(this, ...boundArgs, ...args)
1673
+ return target.apply(
1674
+ that,
1675
+ array_concat.call(args, array_slice.call(arguments))
1676
+ );
1007
1677
 
1008
- addProp(global.Element, "classList", "className");
1009
- addProp(global.HTMLElement, "classList", "className");
1010
- addProp(global.HTMLLinkElement, "relList", "rel");
1011
- addProp(global.HTMLAnchorElement, "relList", "rel");
1012
- addProp(global.HTMLAreaElement, "relList", "rel");
1013
- }(this));
1014
-
1015
- }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1016
-
1017
- function CharacterCount ($module) {
1018
- this.$module = $module;
1019
- this.$textarea = $module.querySelector('.govuk-js-character-count');
1020
- this.$visibleCountMessage = null;
1021
- this.$screenReaderCountMessage = null;
1022
- this.lastInputTimestamp = null;
1023
- }
1024
-
1025
- CharacterCount.prototype.defaults = {
1026
- characterCountAttribute: 'data-maxlength',
1027
- wordCountAttribute: 'data-maxwords'
1028
- };
1029
-
1030
- // Initialize component
1031
- CharacterCount.prototype.init = function () {
1032
- // Check that required elements are present
1033
- if (!this.$textarea) {
1034
- return
1035
- }
1678
+ }
1036
1679
 
1037
- // Check for module
1038
- var $module = this.$module;
1039
- var $textarea = this.$textarea;
1040
- var $fallbackLimitMessage = document.getElementById($textarea.id + '-info');
1041
-
1042
- // Move the fallback count message to be immediately after the textarea
1043
- // Kept for backwards compatibility
1044
- $textarea.insertAdjacentElement('afterend', $fallbackLimitMessage);
1045
-
1046
- // Create the *screen reader* specific live-updating counter
1047
- // This doesn't need any styling classes, as it is never visible
1048
- var $screenReaderCountMessage = document.createElement('div');
1049
- $screenReaderCountMessage.className = 'govuk-character-count__sr-status govuk-visually-hidden';
1050
- $screenReaderCountMessage.setAttribute('aria-live', 'polite');
1051
- this.$screenReaderCountMessage = $screenReaderCountMessage;
1052
- $fallbackLimitMessage.insertAdjacentElement('afterend', $screenReaderCountMessage);
1053
-
1054
- // Create our live-updating counter element, copying the classes from the
1055
- // fallback element for backwards compatibility as these may have been configured
1056
- var $visibleCountMessage = document.createElement('div');
1057
- $visibleCountMessage.className = $fallbackLimitMessage.className;
1058
- $visibleCountMessage.classList.add('govuk-character-count__status');
1059
- $visibleCountMessage.setAttribute('aria-hidden', 'true');
1060
- this.$visibleCountMessage = $visibleCountMessage;
1061
- $fallbackLimitMessage.insertAdjacentElement('afterend', $visibleCountMessage);
1062
-
1063
- // Hide the fallback limit message
1064
- $fallbackLimitMessage.classList.add('govuk-visually-hidden');
1065
-
1066
- // Read options set using dataset ('data-' values)
1067
- this.options = this.getDataset($module);
1068
-
1069
- // Determine the limit attribute (characters or words)
1070
- var countAttribute = this.defaults.characterCountAttribute;
1071
- if (this.options.maxwords) {
1072
- countAttribute = this.defaults.wordCountAttribute;
1073
- }
1680
+ };
1074
1681
 
1075
- // Save the element limit
1076
- this.maxLength = $module.getAttribute(countAttribute);
1682
+ // 15. If the [[Class]] internal property of Target is "Function", then
1683
+ // a. Let L be the length property of Target minus the length of A.
1684
+ // b. Set the length own property of F to either 0 or L, whichever is
1685
+ // larger.
1686
+ // 16. Else set the length own property of F to 0.
1687
+
1688
+ var boundLength = max(0, target.length - args.length);
1689
+
1690
+ // 17. Set the attributes of the length own property of F to the values
1691
+ // specified in 15.3.5.1.
1692
+ var boundArgs = [];
1693
+ for (var i = 0; i < boundLength; i++) {
1694
+ array_push.call(boundArgs, '$' + i);
1695
+ }
1696
+
1697
+ // XXX Build a dynamic function with desired amount of arguments is the only
1698
+ // way to set the length property of a function.
1699
+ // In environments where Content Security Policies enabled (Chrome extensions,
1700
+ // for ex.) all use of eval or Function costructor throws an exception.
1701
+ // However in all of these environments Function.prototype.bind exists
1702
+ // and so this code will never be executed.
1703
+ bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
1704
+
1705
+ if (target.prototype) {
1706
+ Empty.prototype = target.prototype;
1707
+ bound.prototype = new Empty();
1708
+ // Clean up dangling references.
1709
+ Empty.prototype = null;
1710
+ }
1711
+
1712
+ // TODO
1713
+ // 18. Set the [[Extensible]] internal property of F to true.
1714
+
1715
+ // TODO
1716
+ // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
1717
+ // 20. Call the [[DefineOwnProperty]] internal method of F with
1718
+ // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
1719
+ // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
1720
+ // false.
1721
+ // 21. Call the [[DefineOwnProperty]] internal method of F with
1722
+ // arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
1723
+ // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
1724
+ // and false.
1725
+
1726
+ // TODO
1727
+ // NOTE Function objects created using Function.prototype.bind do not
1728
+ // have a prototype property or the [[Code]], [[FormalParameters]], and
1729
+ // [[Scope]] internal properties.
1730
+ // XXX can't delete prototype in pure-js.
1731
+
1732
+ // 22. Return F.
1733
+ return bound;
1734
+ }
1735
+ });
1736
+ })
1737
+ .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1738
+
1739
+ /* eslint-disable es-x/no-date-now -- Polyfill imported */
1740
+
1741
+ /**
1742
+ * @constant
1743
+ * @type {CharacterCountTranslations}
1744
+ * @see Default value for {@link CharacterCountConfig.i18n}
1745
+ * @default
1746
+ */
1747
+ var CHARACTER_COUNT_TRANSLATIONS = {
1748
+ // Characters
1749
+ charactersUnderLimit: {
1750
+ one: 'You have %{count} character remaining',
1751
+ other: 'You have %{count} characters remaining'
1752
+ },
1753
+ charactersAtLimit: 'You have 0 characters remaining',
1754
+ charactersOverLimit: {
1755
+ one: 'You have %{count} character too many',
1756
+ other: 'You have %{count} characters too many'
1757
+ },
1758
+ // Words
1759
+ wordsUnderLimit: {
1760
+ one: 'You have %{count} word remaining',
1761
+ other: 'You have %{count} words remaining'
1762
+ },
1763
+ wordsAtLimit: 'You have 0 words remaining',
1764
+ wordsOverLimit: {
1765
+ one: 'You have %{count} word too many',
1766
+ other: 'You have %{count} words too many'
1767
+ },
1768
+ textareaDescription: {
1769
+ other: ''
1770
+ }
1771
+ };
1772
+
1773
+ /**
1774
+ * JavaScript enhancements for the CharacterCount component
1775
+ *
1776
+ * Tracks the number of characters or words in the `.govuk-js-character-count`
1777
+ * `<textarea>` inside the element. Displays a message with the remaining number
1778
+ * of characters/words available, or the number of characters/words in excess.
1779
+ *
1780
+ * You can configure the message to only appear after a certain percentage
1781
+ * of the available characters/words has been entered.
1782
+ *
1783
+ * @class
1784
+ * @param {HTMLElement} $module - HTML element to use for character count
1785
+ * @param {CharacterCountConfig} [config] - Character count config
1786
+ */
1787
+ function CharacterCount ($module, config) {
1788
+ if (!$module) {
1789
+ return this
1790
+ }
1077
1791
 
1078
- // Check for limit
1079
- if (!this.maxLength) {
1080
- return
1081
- }
1792
+ var defaultConfig = {
1793
+ threshold: 0,
1794
+ i18n: CHARACTER_COUNT_TRANSLATIONS
1795
+ };
1796
+
1797
+ // Read config set using dataset ('data-' values)
1798
+ var datasetConfig = normaliseDataset($module.dataset);
1799
+
1800
+ // To ensure data-attributes take complete precedence, even if they change the
1801
+ // type of count, we need to reset the `maxlength` and `maxwords` from the
1802
+ // JavaScript config.
1803
+ //
1804
+ // We can't mutate `config`, though, as it may be shared across multiple
1805
+ // components inside `initAll`.
1806
+ var configOverrides = {};
1807
+ if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
1808
+ configOverrides = {
1809
+ maxlength: false,
1810
+ maxwords: false
1811
+ };
1812
+ }
1082
1813
 
1083
- // Remove hard limit if set
1084
- $textarea.removeAttribute('maxlength');
1814
+ this.config = mergeConfigs(
1815
+ defaultConfig,
1816
+ config || {},
1817
+ configOverrides,
1818
+ datasetConfig
1819
+ );
1085
1820
 
1086
- this.bindChangeEvents();
1821
+ this.i18n = new I18n(extractConfigByNamespace(this.config, 'i18n'), {
1822
+ // Read the fallback if necessary rather than have it set in the defaults
1823
+ locale: closestAttributeValue($module, 'lang')
1824
+ });
1825
+
1826
+ // Determine the limit attribute (characters or words)
1827
+ if (this.config.maxwords) {
1828
+ this.maxLength = this.config.maxwords;
1829
+ } else if (this.config.maxlength) {
1830
+ this.maxLength = this.config.maxlength;
1831
+ } else {
1832
+ return
1833
+ }
1087
1834
 
1088
- // When the page is restored after navigating 'back' in some browsers the
1089
- // state of the character count is not restored until *after* the DOMContentLoaded
1090
- // event is fired, so we need to manually update it after the pageshow event
1091
- // in browsers that support it.
1092
- if ('onpageshow' in window) {
1093
- window.addEventListener('pageshow', this.updateCountMessage.bind(this));
1094
- } else {
1095
- window.addEventListener('DOMContentLoaded', this.updateCountMessage.bind(this));
1835
+ this.$module = $module;
1836
+ this.$textarea = $module.querySelector('.govuk-js-character-count');
1837
+ this.$visibleCountMessage = null;
1838
+ this.$screenReaderCountMessage = null;
1839
+ this.lastInputTimestamp = null;
1096
1840
  }
1097
- this.updateCountMessage();
1098
- };
1099
-
1100
- // Read data attributes
1101
- CharacterCount.prototype.getDataset = function (element) {
1102
- var dataset = {};
1103
- var attributes = element.attributes;
1104
- if (attributes) {
1105
- for (var i = 0; i < attributes.length; i++) {
1106
- var attribute = attributes[i];
1107
- var match = attribute.name.match(/^data-(.+)/);
1108
- if (match) {
1109
- dataset[match[1]] = attribute.value;
1110
- }
1841
+
1842
+ /**
1843
+ * Initialise component
1844
+ */
1845
+ CharacterCount.prototype.init = function () {
1846
+ // Check that required elements are present
1847
+ if (!this.$textarea) {
1848
+ return
1111
1849
  }
1112
- }
1113
- return dataset
1114
- };
1115
-
1116
- // Counts characters or words in text
1117
- CharacterCount.prototype.count = function (text) {
1118
- var length;
1119
- if (this.options.maxwords) {
1120
- var tokens = text.match(/\S+/g) || []; // Matches consecutive non-whitespace chars
1121
- length = tokens.length;
1122
- } else {
1123
- length = text.length;
1124
- }
1125
- return length
1126
- };
1127
-
1128
- // Bind input propertychange to the elements and update based on the change
1129
- CharacterCount.prototype.bindChangeEvents = function () {
1130
- var $textarea = this.$textarea;
1131
- $textarea.addEventListener('keyup', this.handleKeyUp.bind(this));
1132
-
1133
- // Bind focus/blur events to start/stop polling
1134
- $textarea.addEventListener('focus', this.handleFocus.bind(this));
1135
- $textarea.addEventListener('blur', this.handleBlur.bind(this));
1136
- };
1137
-
1138
- // Speech recognition software such as Dragon NaturallySpeaking will modify the
1139
- // fields by directly changing its `value`. These changes don't trigger events
1140
- // in JavaScript, so we need to poll to handle when and if they occur.
1141
- CharacterCount.prototype.checkIfValueChanged = function () {
1142
- if (!this.$textarea.oldValue) this.$textarea.oldValue = '';
1143
- if (this.$textarea.value !== this.$textarea.oldValue) {
1144
- this.$textarea.oldValue = this.$textarea.value;
1145
- this.updateCountMessage();
1146
- }
1147
- };
1148
-
1149
- // Helper function to update both the visible and screen reader-specific
1150
- // counters simultaneously (e.g. on init)
1151
- CharacterCount.prototype.updateCountMessage = function () {
1152
- this.updateVisibleCountMessage();
1153
- this.updateScreenReaderCountMessage();
1154
- };
1155
-
1156
- // Update visible counter
1157
- CharacterCount.prototype.updateVisibleCountMessage = function () {
1158
- var $textarea = this.$textarea;
1159
- var $visibleCountMessage = this.$visibleCountMessage;
1160
- var remainingNumber = this.maxLength - this.count($textarea.value);
1161
-
1162
- // If input is over the threshold, remove the disabled class which renders the
1163
- // counter invisible.
1164
- if (this.isOverThreshold()) {
1165
- $visibleCountMessage.classList.remove('govuk-character-count__message--disabled');
1166
- } else {
1167
- $visibleCountMessage.classList.add('govuk-character-count__message--disabled');
1168
- }
1169
1850
 
1170
- // Update styles
1171
- if (remainingNumber < 0) {
1172
- $textarea.classList.add('govuk-textarea--error');
1173
- $visibleCountMessage.classList.remove('govuk-hint');
1174
- $visibleCountMessage.classList.add('govuk-error-message');
1175
- } else {
1176
- $textarea.classList.remove('govuk-textarea--error');
1177
- $visibleCountMessage.classList.remove('govuk-error-message');
1178
- $visibleCountMessage.classList.add('govuk-hint');
1179
- }
1851
+ var $textarea = this.$textarea;
1852
+ var $textareaDescription = document.getElementById($textarea.id + '-info');
1853
+
1854
+ // Inject a decription for the textarea if none is present already
1855
+ // for when the component was rendered with no maxlength, maxwords
1856
+ // nor custom textareaDescriptionText
1857
+ if ($textareaDescription.innerText.match(/^\s*$/)) {
1858
+ $textareaDescription.innerText = this.i18n.t('textareaDescription', { count: this.maxLength });
1859
+ }
1180
1860
 
1181
- // Update message
1182
- $visibleCountMessage.innerHTML = this.formattedUpdateMessage();
1183
- };
1861
+ // Move the textarea description to be immediately after the textarea
1862
+ // Kept for backwards compatibility
1863
+ $textarea.insertAdjacentElement('afterend', $textareaDescription);
1864
+
1865
+ // Create the *screen reader* specific live-updating counter
1866
+ // This doesn't need any styling classes, as it is never visible
1867
+ var $screenReaderCountMessage = document.createElement('div');
1868
+ $screenReaderCountMessage.className = 'govuk-character-count__sr-status govuk-visually-hidden';
1869
+ $screenReaderCountMessage.setAttribute('aria-live', 'polite');
1870
+ this.$screenReaderCountMessage = $screenReaderCountMessage;
1871
+ $textareaDescription.insertAdjacentElement('afterend', $screenReaderCountMessage);
1872
+
1873
+ // Create our live-updating counter element, copying the classes from the
1874
+ // textarea description for backwards compatibility as these may have been
1875
+ // configured
1876
+ var $visibleCountMessage = document.createElement('div');
1877
+ $visibleCountMessage.className = $textareaDescription.className;
1878
+ $visibleCountMessage.classList.add('govuk-character-count__status');
1879
+ $visibleCountMessage.setAttribute('aria-hidden', 'true');
1880
+ this.$visibleCountMessage = $visibleCountMessage;
1881
+ $textareaDescription.insertAdjacentElement('afterend', $visibleCountMessage);
1882
+
1883
+ // Hide the textarea description
1884
+ $textareaDescription.classList.add('govuk-visually-hidden');
1885
+
1886
+ // Remove hard limit if set
1887
+ $textarea.removeAttribute('maxlength');
1888
+
1889
+ this.bindChangeEvents();
1890
+
1891
+ // When the page is restored after navigating 'back' in some browsers the
1892
+ // state of the character count is not restored until *after* the
1893
+ // DOMContentLoaded event is fired, so we need to manually update it after the
1894
+ // pageshow event in browsers that support it.
1895
+ if ('onpageshow' in window) {
1896
+ window.addEventListener('pageshow', this.updateCountMessage.bind(this));
1897
+ } else {
1898
+ window.addEventListener('DOMContentLoaded', this.updateCountMessage.bind(this));
1899
+ }
1900
+ this.updateCountMessage();
1901
+ };
1902
+
1903
+ /**
1904
+ * Bind change events
1905
+ *
1906
+ * Set up event listeners on the $textarea so that the count messages update
1907
+ * when the user types.
1908
+ */
1909
+ CharacterCount.prototype.bindChangeEvents = function () {
1910
+ var $textarea = this.$textarea;
1911
+ $textarea.addEventListener('keyup', this.handleKeyUp.bind(this));
1912
+
1913
+ // Bind focus/blur events to start/stop polling
1914
+ $textarea.addEventListener('focus', this.handleFocus.bind(this));
1915
+ $textarea.addEventListener('blur', this.handleBlur.bind(this));
1916
+ };
1917
+
1918
+ /**
1919
+ * Handle key up event
1920
+ *
1921
+ * Update the visible character counter and keep track of when the last update
1922
+ * happened for each keypress
1923
+ */
1924
+ CharacterCount.prototype.handleKeyUp = function () {
1925
+ this.updateVisibleCountMessage();
1926
+ this.lastInputTimestamp = Date.now();
1927
+ };
1928
+
1929
+ /**
1930
+ * Handle focus event
1931
+ *
1932
+ * Speech recognition software such as Dragon NaturallySpeaking will modify the
1933
+ * fields by directly changing its `value`. These changes don't trigger events
1934
+ * in JavaScript, so we need to poll to handle when and if they occur.
1935
+ *
1936
+ * Once the keyup event hasn't been detected for at least 1000 ms (1s), check if
1937
+ * the textarea value has changed and update the count message if it has.
1938
+ *
1939
+ * This is so that the update triggered by the manual comparison doesn't
1940
+ * conflict with debounced KeyboardEvent updates.
1941
+ */
1942
+ CharacterCount.prototype.handleFocus = function () {
1943
+ this.valueChecker = setInterval(function () {
1944
+ if (!this.lastInputTimestamp || (Date.now() - 500) >= this.lastInputTimestamp) {
1945
+ this.updateIfValueChanged();
1946
+ }
1947
+ }.bind(this), 1000);
1948
+ };
1949
+
1950
+ /**
1951
+ * Handle blur event
1952
+ *
1953
+ * Stop checking the textarea value once the textarea no longer has focus
1954
+ */
1955
+ CharacterCount.prototype.handleBlur = function () {
1956
+ // Cancel value checking on blur
1957
+ clearInterval(this.valueChecker);
1958
+ };
1959
+
1960
+ /**
1961
+ * Update count message if textarea value has changed
1962
+ */
1963
+ CharacterCount.prototype.updateIfValueChanged = function () {
1964
+ if (!this.$textarea.oldValue) this.$textarea.oldValue = '';
1965
+ if (this.$textarea.value !== this.$textarea.oldValue) {
1966
+ this.$textarea.oldValue = this.$textarea.value;
1967
+ this.updateCountMessage();
1968
+ }
1969
+ };
1970
+
1971
+ /**
1972
+ * Update count message
1973
+ *
1974
+ * Helper function to update both the visible and screen reader-specific
1975
+ * counters simultaneously (e.g. on init)
1976
+ */
1977
+ CharacterCount.prototype.updateCountMessage = function () {
1978
+ this.updateVisibleCountMessage();
1979
+ this.updateScreenReaderCountMessage();
1980
+ };
1981
+
1982
+ /**
1983
+ * Update visible count message
1984
+ */
1985
+ CharacterCount.prototype.updateVisibleCountMessage = function () {
1986
+ var $textarea = this.$textarea;
1987
+ var $visibleCountMessage = this.$visibleCountMessage;
1988
+ var remainingNumber = this.maxLength - this.count($textarea.value);
1989
+
1990
+ // If input is over the threshold, remove the disabled class which renders the
1991
+ // counter invisible.
1992
+ if (this.isOverThreshold()) {
1993
+ $visibleCountMessage.classList.remove('govuk-character-count__message--disabled');
1994
+ } else {
1995
+ $visibleCountMessage.classList.add('govuk-character-count__message--disabled');
1996
+ }
1184
1997
 
1185
- // Update screen reader-specific counter
1186
- CharacterCount.prototype.updateScreenReaderCountMessage = function () {
1187
- var $screenReaderCountMessage = this.$screenReaderCountMessage;
1998
+ // Update styles
1999
+ if (remainingNumber < 0) {
2000
+ $textarea.classList.add('govuk-textarea--error');
2001
+ $visibleCountMessage.classList.remove('govuk-hint');
2002
+ $visibleCountMessage.classList.add('govuk-error-message');
2003
+ } else {
2004
+ $textarea.classList.remove('govuk-textarea--error');
2005
+ $visibleCountMessage.classList.remove('govuk-error-message');
2006
+ $visibleCountMessage.classList.add('govuk-hint');
2007
+ }
1188
2008
 
1189
- // If over the threshold, remove the aria-hidden attribute, allowing screen
1190
- // readers to announce the content of the element.
1191
- if (this.isOverThreshold()) {
1192
- $screenReaderCountMessage.removeAttribute('aria-hidden');
1193
- } else {
1194
- $screenReaderCountMessage.setAttribute('aria-hidden', true);
1195
- }
2009
+ // Update message
2010
+ $visibleCountMessage.innerText = this.getCountMessage();
2011
+ };
2012
+
2013
+ /**
2014
+ * Update screen reader count message
2015
+ */
2016
+ CharacterCount.prototype.updateScreenReaderCountMessage = function () {
2017
+ var $screenReaderCountMessage = this.$screenReaderCountMessage;
2018
+
2019
+ // If over the threshold, remove the aria-hidden attribute, allowing screen
2020
+ // readers to announce the content of the element.
2021
+ if (this.isOverThreshold()) {
2022
+ $screenReaderCountMessage.removeAttribute('aria-hidden');
2023
+ } else {
2024
+ $screenReaderCountMessage.setAttribute('aria-hidden', true);
2025
+ }
1196
2026
 
1197
- // Update message
1198
- $screenReaderCountMessage.innerHTML = this.formattedUpdateMessage();
1199
- };
1200
-
1201
- // Format update message
1202
- CharacterCount.prototype.formattedUpdateMessage = function () {
1203
- var $textarea = this.$textarea;
1204
- var options = this.options;
1205
- var remainingNumber = this.maxLength - this.count($textarea.value);
1206
-
1207
- var charVerb = 'remaining';
1208
- var charNoun = 'character';
1209
- var displayNumber = remainingNumber;
1210
- if (options.maxwords) {
1211
- charNoun = 'word';
1212
- }
1213
- charNoun = charNoun + ((remainingNumber === -1 || remainingNumber === 1) ? '' : 's');
1214
-
1215
- charVerb = (remainingNumber < 0) ? 'too many' : 'remaining';
1216
- displayNumber = Math.abs(remainingNumber);
1217
-
1218
- return 'You have ' + displayNumber + ' ' + charNoun + ' ' + charVerb
1219
- };
1220
-
1221
- // Checks whether the value is over the configured threshold for the input.
1222
- // If there is no configured threshold, it is set to 0 and this function will
1223
- // always return true.
1224
- CharacterCount.prototype.isOverThreshold = function () {
1225
- var $textarea = this.$textarea;
1226
- var options = this.options;
1227
-
1228
- // Determine the remaining number of characters/words
1229
- var currentLength = this.count($textarea.value);
1230
- var maxLength = this.maxLength;
1231
-
1232
- // Set threshold if presented in options
1233
- var thresholdPercent = options.threshold ? options.threshold : 0;
1234
- var thresholdValue = maxLength * thresholdPercent / 100;
1235
-
1236
- return (thresholdValue <= currentLength)
1237
- };
1238
-
1239
- // Update the visible character counter and keep track of when the last update
1240
- // happened for each keypress
1241
- CharacterCount.prototype.handleKeyUp = function () {
1242
- this.updateVisibleCountMessage();
1243
- this.lastInputTimestamp = Date.now();
1244
- };
1245
-
1246
- CharacterCount.prototype.handleFocus = function () {
1247
- // If the field is focused, and a keyup event hasn't been detected for at
1248
- // least 1000 ms (1 second), then run the manual change check.
1249
- // This is so that the update triggered by the manual comparison doesn't
1250
- // conflict with debounced KeyboardEvent updates.
1251
- this.valueChecker = setInterval(function () {
1252
- if (!this.lastInputTimestamp || (Date.now() - 500) >= this.lastInputTimestamp) {
1253
- this.checkIfValueChanged();
2027
+ // Update message
2028
+ $screenReaderCountMessage.innerText = this.getCountMessage();
2029
+ };
2030
+
2031
+ /**
2032
+ * Count the number of characters (or words, if `config.maxwords` is set)
2033
+ * in the given text
2034
+ *
2035
+ * @param {string} text - The text to count the characters of
2036
+ * @returns {number} the number of characters (or words) in the text
2037
+ */
2038
+ CharacterCount.prototype.count = function (text) {
2039
+ if (this.config.maxwords) {
2040
+ var tokens = text.match(/\S+/g) || []; // Matches consecutive non-whitespace chars
2041
+ return tokens.length
2042
+ } else {
2043
+ return text.length
2044
+ }
2045
+ };
2046
+
2047
+ /**
2048
+ * Get count message
2049
+ *
2050
+ * @returns {string} Status message
2051
+ */
2052
+ CharacterCount.prototype.getCountMessage = function () {
2053
+ var remainingNumber = this.maxLength - this.count(this.$textarea.value);
2054
+
2055
+ var countType = this.config.maxwords ? 'words' : 'characters';
2056
+ return this.formatCountMessage(remainingNumber, countType)
2057
+ };
2058
+
2059
+ /**
2060
+ * Formats the message shown to users according to what's counted
2061
+ * and how many remain
2062
+ *
2063
+ * @param {number} remainingNumber - The number of words/characaters remaining
2064
+ * @param {string} countType - "words" or "characters"
2065
+ * @returns {string} Status message
2066
+ */
2067
+ CharacterCount.prototype.formatCountMessage = function (remainingNumber, countType) {
2068
+ if (remainingNumber === 0) {
2069
+ return this.i18n.t(countType + 'AtLimit')
1254
2070
  }
1255
- }.bind(this), 1000);
1256
- };
1257
2071
 
1258
- CharacterCount.prototype.handleBlur = function () {
1259
- // Cancel value checking on blur
1260
- clearInterval(this.valueChecker);
1261
- };
2072
+ var translationKeySuffix = remainingNumber < 0 ? 'OverLimit' : 'UnderLimit';
2073
+
2074
+ return this.i18n.t(countType + translationKeySuffix, { count: Math.abs(remainingNumber) })
2075
+ };
2076
+
2077
+ /**
2078
+ * Check if count is over threshold
2079
+ *
2080
+ * Checks whether the value is over the configured threshold for the input.
2081
+ * If there is no configured threshold, it is set to 0 and this function will
2082
+ * always return true.
2083
+ *
2084
+ * @returns {boolean} true if the current count is over the config.threshold
2085
+ * (or no threshold is set)
2086
+ */
2087
+ CharacterCount.prototype.isOverThreshold = function () {
2088
+ // No threshold means we're always above threshold so save some computation
2089
+ if (!this.config.threshold) {
2090
+ return true
2091
+ }
1262
2092
 
1263
- return CharacterCount;
2093
+ var $textarea = this.$textarea;
2094
+
2095
+ // Determine the remaining number of characters/words
2096
+ var currentLength = this.count($textarea.value);
2097
+ var maxLength = this.maxLength;
2098
+
2099
+ var thresholdValue = maxLength * this.config.threshold / 100;
2100
+
2101
+ return (thresholdValue <= currentLength)
2102
+ };
2103
+
2104
+ /**
2105
+ * Character count config
2106
+ *
2107
+ * @typedef {CharacterCountConfigWithMaxLength | CharacterCountConfigWithMaxWords} CharacterCountConfig
2108
+ */
2109
+
2110
+ /**
2111
+ * Character count config (with maximum number of characters)
2112
+ *
2113
+ * @typedef {object} CharacterCountConfigWithMaxLength
2114
+ * @property {number} [maxlength] - The maximum number of characters.
2115
+ * If maxwords is provided, the maxlength option will be ignored.
2116
+ * @property {number} [threshold = 0] - The percentage value of the limit at
2117
+ * which point the count message is displayed. If this attribute is set, the
2118
+ * count message will be hidden by default.
2119
+ * @property {CharacterCountTranslations} [i18n = CHARACTER_COUNT_TRANSLATIONS] - See constant {@link CHARACTER_COUNT_TRANSLATIONS}
2120
+ */
2121
+
2122
+ /**
2123
+ * Character count config (with maximum number of words)
2124
+ *
2125
+ * @typedef {object} CharacterCountConfigWithMaxWords
2126
+ * @property {number} [maxwords] - The maximum number of words. If maxwords is
2127
+ * provided, the maxlength option will be ignored.
2128
+ * @property {number} [threshold = 0] - The percentage value of the limit at
2129
+ * which point the count message is displayed. If this attribute is set, the
2130
+ * count message will be hidden by default.
2131
+ * @property {CharacterCountTranslations} [i18n = CHARACTER_COUNT_TRANSLATIONS] - See constant {@link CHARACTER_COUNT_TRANSLATIONS}
2132
+ */
2133
+
2134
+ /**
2135
+ * Character count translations
2136
+ *
2137
+ * @typedef {object} CharacterCountTranslations
2138
+ *
2139
+ * Messages shown to users as they type. It provides feedback on how many words
2140
+ * or characters they have remaining or if they are over the limit. This also
2141
+ * includes a message used as an accessible description for the textarea.
2142
+ * @property {TranslationPluralForms} [charactersUnderLimit] - Message displayed
2143
+ * when the number of characters is under the configured maximum, `maxlength`.
2144
+ * This message is displayed visually and through assistive technologies. The
2145
+ * component will replace the `%{count}` placeholder with the number of
2146
+ * remaining characters. This is a [pluralised list of
2147
+ * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).
2148
+ * @property {string} [charactersAtLimit] - Message displayed when the number of
2149
+ * characters reaches the configured maximum, `maxlength`. This message is
2150
+ * displayed visually and through assistive technologies.
2151
+ * @property {TranslationPluralForms} [charactersOverLimit] - Message displayed
2152
+ * when the number of characters is over the configured maximum, `maxlength`.
2153
+ * This message is displayed visually and through assistive technologies. The
2154
+ * component will replace the `%{count}` placeholder with the number of
2155
+ * remaining characters. This is a [pluralised list of
2156
+ * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).
2157
+ * @property {TranslationPluralForms} [wordsUnderLimit] - Message displayed when
2158
+ * the number of words is under the configured maximum, `maxlength`. This
2159
+ * message is displayed visually and through assistive technologies. The
2160
+ * component will replace the `%{count}` placeholder with the number of
2161
+ * remaining words. This is a [pluralised list of
2162
+ * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).
2163
+ * @property {string} [wordsAtLimit] - Message displayed when the number of
2164
+ * words reaches the configured maximum, `maxlength`. This message is
2165
+ * displayed visually and through assistive technologies.
2166
+ * @property {TranslationPluralForms} [wordsOverLimit] - Message displayed when
2167
+ * the number of words is over the configured maximum, `maxlength`. This
2168
+ * message is displayed visually and through assistive technologies. The
2169
+ * component will replace the `%{count}` placeholder with the number of
2170
+ * remaining words. This is a [pluralised list of
2171
+ * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).
2172
+ * @property {TranslationPluralForms} [textareaDescription] - Message made
2173
+ * available to assistive technologies, if none is already present in the
2174
+ * HTML, to describe that the component accepts only a limited amount of
2175
+ * content. It is visible on the page when JavaScript is unavailable. The
2176
+ * component will replace the `%{count}` placeholder with the value of the
2177
+ * `maxlength` or `maxwords` parameter.
2178
+ */
2179
+
2180
+ /**
2181
+ * @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
2182
+ */
2183
+
2184
+ return CharacterCount;
1264
2185
 
1265
2186
  })));
2187
+ //# sourceMappingURL=character-count.js.map