j1-template 2023.5.2 → 2023.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/_includes/themes/j1/layouts/content_generator_news_panel_posts.html +11 -9
  3. data/_includes/themes/j1/layouts/content_generator_post.html +8 -6
  4. data/_includes/themes/j1/procedures/global/create_word_cloud.proc +1 -1
  5. data/_includes/themes/j1/procedures/layouts/module_writer.proc +1 -1
  6. data/_includes/themes/j1/procedures/posts/collate_timeline.proc +4 -4
  7. data/_includes/themes/j1/procedures/posts/pager.proc +1 -1
  8. data/_layouts/_home.html +88 -0
  9. data/_layouts/home.html +1 -1
  10. data/assets/data/banner.html +7 -7
  11. data/assets/data/panel.html +37 -25
  12. data/assets/data/quicklinks.html +40 -30
  13. data/assets/data/speak2me.html +219 -0
  14. data/assets/data/translator.html +32 -4
  15. data/assets/themes/j1/adapter/js/speak2me.js +425 -0
  16. data/assets/themes/j1/adapter/js/translator.js +10 -2
  17. data/assets/themes/j1/core/css/icon-fonts/mdib.css +21 -0
  18. data/assets/themes/j1/core/css/icon-fonts/mdib.min.css +1 -1
  19. data/assets/themes/j1/core/css/themes/bootstrap/bootstrap.css +34 -40
  20. data/assets/themes/j1/core/css/themes/bootstrap/bootstrap.min.css +2 -2
  21. data/assets/themes/j1/core/css/themes/unodark/bootstrap.css +77 -57
  22. data/assets/themes/j1/core/css/themes/unodark/bootstrap.min.css +2 -2
  23. data/assets/themes/j1/core/css/themes/unolight/bootstrap.css +176 -42
  24. data/assets/themes/j1/core/css/themes/unolight/bootstrap.min.css +3 -3
  25. data/assets/themes/j1/core/js/template.js +1658 -0
  26. data/assets/themes/j1/core/js/template.min.js +11 -5
  27. data/assets/themes/j1/core/js/template.min.js.map +1 -1
  28. data/assets/themes/j1/modules/speak2me/LICENSE +21 -0
  29. data/assets/themes/j1/modules/speak2me/js/speak2me.js +947 -0
  30. data/assets/themes/j1/modules/speak2me/js/speak2me.min.js +26 -0
  31. data/assets/themes/j1/modules/translator/js/translator.js +30 -13
  32. data/assets/themes/j1/modules/translator/js/translator.min.js +1 -1
  33. data/lib/j1/version.rb +1 -1
  34. data/lib/starter_web/Gemfile +5 -5
  35. data/lib/starter_web/README.md +5 -5
  36. data/lib/starter_web/_config.yml +12 -44
  37. data/lib/starter_web/_data/blocks/banner.yml +7 -6
  38. data/lib/starter_web/_data/blocks/panel.yml +37 -39
  39. data/lib/starter_web/_data/layouts/home.yml +1 -1
  40. data/lib/starter_web/_data/modules/advertising.yml +10 -88
  41. data/lib/starter_web/_data/modules/buymeacoffee.yml +30 -0
  42. data/lib/starter_web/_data/modules/defaults/advertising.yml +3 -1
  43. data/lib/starter_web/_data/modules/defaults/blog_navigator.yml +97 -164
  44. data/lib/starter_web/_data/modules/defaults/buymeacoffee.yml +30 -0
  45. data/lib/starter_web/_data/modules/defaults/navigator.yml +8 -4
  46. data/lib/starter_web/_data/modules/defaults/speak2me.yml +72 -0
  47. data/lib/starter_web/_data/modules/scroller.yml +1 -1
  48. data/lib/starter_web/_data/modules/speak2me.yml +33 -0
  49. data/lib/starter_web/_data/resources.yml +24 -1
  50. data/lib/starter_web/_data/templates/feed.xml +1 -1
  51. data/lib/starter_web/_includes/google/static/{google_ad_5128488466.html → google_ad__your-slot-id.html} +1 -1
  52. data/lib/starter_web/_plugins/asciidoctor/callout.rb +1 -1
  53. data/lib/starter_web/_plugins/asciidoctor/carousel-block.rb +2 -2
  54. data/lib/starter_web/_plugins/asciidoctor/gallery-block.rb +2 -2
  55. data/lib/starter_web/_plugins/asciidoctor/gist-block.rb +2 -2
  56. data/lib/starter_web/_plugins/asciidoctor/google-ad-block.rb +1 -1
  57. data/lib/starter_web/_plugins/asciidoctor/lightbox-block.rb +2 -7
  58. data/lib/starter_web/_plugins/asciidoctor/masonry-block.rb +2 -3
  59. data/lib/starter_web/_plugins/asciidoctor/masterslider-block.rb +2 -2
  60. data/lib/starter_web/_plugins/asciidoctor/mdib-icon-inline.rb +43 -0
  61. data/lib/starter_web/_plugins/asciidoctor/mdil-icon-inline.rb +1 -1
  62. data/lib/starter_web/_plugins/asciidoctor/range-slider-block.rb +1 -1
  63. data/lib/starter_web/_plugins/asciidoctor/slick-block.rb +2 -2
  64. data/lib/starter_web/_plugins/asciidoctor/textbook-block.rb +1 -1
  65. data/lib/starter_web/_plugins/filter/liquify.rb +22 -22
  66. data/lib/starter_web/_plugins/index/lunr.rb +1 -1
  67. data/lib/starter_web/assets/images/collections/books/biography/a_life_in_questions.jpg +0 -0
  68. data/lib/starter_web/assets/images/collections/books/biography/becoming.jpg +0 -0
  69. data/lib/starter_web/assets/images/collections/books/biography/born_to_run.jpg +0 -0
  70. data/lib/starter_web/assets/images/collections/books/biography/forty_autumns.jpg +0 -0
  71. data/lib/starter_web/assets/images/collections/books/biography/not_dead_yet.jpg +0 -0
  72. data/lib/starter_web/assets/images/collections/books/biography/the_princess_diarist.jpg +0 -0
  73. data/lib/starter_web/assets/images/collections/books/biography/when_breath_becomes_air.jpg +0 -0
  74. data/lib/starter_web/assets/images/collections/books/fantasy/harry-potter-deathly-hallows.jpg +0 -0
  75. data/lib/starter_web/assets/images/collections/books/fantasy/harry-potter-philosophers-stone.jpg +0 -0
  76. data/lib/starter_web/assets/images/collections/books/fantasy/mistborn-trilogy.jpg +0 -0
  77. data/lib/starter_web/assets/images/collections/books/fantasy/ready-player-one.jpg +0 -0
  78. data/lib/starter_web/assets/images/collections/books/fantasy/sword-of-destiny.jpg +0 -0
  79. data/lib/starter_web/assets/images/collections/books/fantasy/terry-pratchet-diary.jpg +0 -0
  80. data/lib/starter_web/assets/images/collections/books/romance/breath-of-snow-and-ashes.jpg +0 -0
  81. data/lib/starter_web/assets/images/collections/books/romance/it-ends-with-us.jpg +0 -0
  82. data/lib/starter_web/assets/images/collections/books/romance/outlander-novel.jpg +0 -0
  83. data/lib/starter_web/assets/images/collections/books/romance/outlander-short-story.jpg +0 -0
  84. data/lib/starter_web/assets/images/collections/books/romance/the-fiery-cross.jpg +0 -0
  85. data/lib/starter_web/assets/images/collections/books/romance/the_dressmaker.jpg +0 -0
  86. data/lib/starter_web/collections/_biography/a-life-in-questions.adoc +13 -8
  87. data/lib/starter_web/collections/_biography/becoming.adoc +18 -12
  88. data/lib/starter_web/collections/_biography/born-to-run.adoc +17 -13
  89. data/lib/starter_web/collections/_biography/forty-autumns.adoc +14 -9
  90. data/lib/starter_web/collections/_biography/not-dead-yet.adoc +12 -7
  91. data/lib/starter_web/collections/_biography/princess-diarist-the.adoc +13 -8
  92. data/lib/starter_web/collections/_biography/when-breath-becomes-air.adoc +14 -9
  93. data/lib/starter_web/collections/_fantasy/harry-potter-deathly-hallows.adoc +13 -7
  94. data/lib/starter_web/collections/_fantasy/harry-potter-philosophers-stone.adoc +12 -8
  95. data/lib/starter_web/collections/_fantasy/mistborn-trilogy.adoc +14 -10
  96. data/lib/starter_web/collections/_fantasy/ready-player-one.adoc +12 -11
  97. data/lib/starter_web/collections/_fantasy/sword-of-destiny.adoc +15 -7
  98. data/lib/starter_web/collections/_fantasy/terry-pratchet-diary.adoc +11 -6
  99. data/lib/starter_web/collections/_romance/breath-of-snow-and-ashes.adoc +15 -10
  100. data/lib/starter_web/collections/_romance/it-ends-with-us.adoc +11 -6
  101. data/lib/starter_web/collections/_romance/outlander-novel.adoc +11 -6
  102. data/lib/starter_web/collections/_romance/{virgins-outlander-short-story.adoc → outlander-virgins-short-story.adoc} +11 -6
  103. data/lib/starter_web/collections/_romance/{dressmaker-the.adoc → the-dressmaker.adoc} +11 -6
  104. data/lib/starter_web/collections/_romance/{fiery-cross-the.adoc → the-fiery-cross.adoc} +11 -6
  105. data/lib/starter_web/collections/posts/public/featured/_posts/0000-00-00-welcome-to-j1.adoc.erb +6 -6
  106. data/lib/starter_web/collections/posts/public/featured/_posts/2021-01-01-about-cookies.adoc +12 -11
  107. data/lib/starter_web/collections/posts/public/featured/_posts/2021-02-01-static-site-generators.adoc +9 -7
  108. data/lib/starter_web/collections/posts/public/featured/_posts/2022-02-01-about-j1.adoc +8 -8
  109. data/lib/starter_web/dot.ruby-version +1 -1
  110. data/lib/starter_web/index.html +10 -8
  111. data/lib/starter_web/package.json +1 -1
  112. data/lib/starter_web/pages/public/about/features.adoc +7 -1
  113. data/lib/starter_web/pages/public/about/reporting_issues.adoc +2 -0
  114. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/000_intro.adoc +2 -0
  115. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/100_converter.adoc +2 -0
  116. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/200_themes.adoc +2 -0
  117. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/_includes/documents/000_intro.asciidoc +1 -1
  118. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/_includes/documents/100_converter/112_getting_started.asciidoc +1 -0
  119. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/_includes/documents/100_converter/113_themes.asciidoc +1 -0
  120. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/documentation.adoc +2 -0
  121. data/lib/starter_web/pages/public/asciidoc_skeletons/multi-document/_includes/documents/100_chapter.asciidoc +3 -1
  122. data/lib/starter_web/pages/public/asciidoc_skeletons/multi-document/_includes/documents/200_chapter.asciidoc +1 -0
  123. data/lib/starter_web/pages/public/asciidoc_skeletons/multi-document/multi.adoc +2 -0
  124. data/lib/starter_web/pages/public/asciidoc_skeletons/simple-document/simple.adoc +6 -0
  125. data/lib/starter_web/pages/public/blog/navigator/archive/allview.html +6 -13
  126. data/lib/starter_web/pages/public/blog/navigator/archive/categoryview.html +17 -11
  127. data/lib/starter_web/pages/public/blog/navigator/archive/dateview.html +7 -8
  128. data/lib/starter_web/pages/public/blog/navigator/archive/tagview.html +5 -7
  129. data/lib/starter_web/pages/public/blog/navigator/index.html +15 -14
  130. data/lib/starter_web/pages/public/features/general.adoc +7 -1
  131. data/lib/starter_web/pages/public/features/template.adoc +87 -128
  132. data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_all_books.adoc +10 -2
  133. data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_biography.adoc +8 -3
  134. data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_fantasy.adoc +11 -4
  135. data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_romance.adoc +11 -5
  136. data/lib/starter_web/pages/public/learn/bookshelf/jekyll_collections.adoc +24 -14
  137. data/lib/starter_web/pages/public/learn/bs_sass_variables/bs_sass_variables.adoc +4 -0
  138. data/lib/starter_web/pages/public/learn/core_web_vitals/core_web_vitals.adoc +8 -4
  139. data/lib/starter_web/pages/public/learn/roundtrip/_includes/documents/100_gistblock.asciidoc +2 -1
  140. data/lib/starter_web/pages/public/learn/roundtrip/_includes/documents/themes_bootstrap.asciidoc +27 -28
  141. data/lib/starter_web/pages/public/learn/roundtrip/asciidoc_extensions.adoc +169 -155
  142. data/lib/starter_web/pages/public/learn/roundtrip/bootstrap_themes.adoc +28 -19
  143. data/lib/starter_web/pages/public/learn/roundtrip/highlghter_rouge.adoc +9 -5
  144. data/lib/starter_web/pages/public/learn/roundtrip/icon_fonts.adoc +67 -77
  145. data/lib/starter_web/pages/public/learn/roundtrip/lunr_search.1.asciidoc +460 -0
  146. data/lib/starter_web/pages/public/learn/roundtrip/lunr_search.adoc +12 -7
  147. data/lib/starter_web/pages/public/learn/roundtrip/modal_extentions.adoc +26 -19
  148. data/lib/starter_web/pages/public/learn/roundtrip/present_images.adoc +470 -447
  149. data/lib/starter_web/pages/public/learn/roundtrip/present_videos.adoc +60 -59
  150. data/lib/starter_web/pages/public/learn/roundtrip/responsive_tables.adoc +51 -38
  151. data/lib/starter_web/pages/public/learn/roundtrip/typography.adoc +31 -25
  152. data/lib/starter_web/pages/public/learn/where_to_go.adoc +9 -4
  153. data/lib/starter_web/pages/public/legal/en/100_copyright.adoc +4 -0
  154. data/lib/starter_web/pages/public/legal/en/200_impress.adoc +2 -0
  155. data/lib/starter_web/pages/public/legal/en/300_privacy.adoc +22 -3
  156. data/lib/starter_web/pages/public/legal/en/400_comment_policy.adoc +5 -0
  157. data/lib/starter_web/pages/public/manuals/speak2me.adoc +412 -0
  158. data/lib/starter_web/pages/public/panels/intro_panel/panel.adoc +27 -19
  159. data/lib/starter_web/pages/public/plans/plans.adoc +3 -0
  160. data/lib/starter_web/pages/public/tools/cheatsheet/gem.adoc +2 -0
  161. data/lib/starter_web/pages/public/tools/cheatsheet/git.adoc +2 -0
  162. data/lib/starter_web/pages/public/tools/cheatsheet/j1.adoc +2 -0
  163. data/lib/starter_web/pages/public/tools/cheatsheet/yaml.adoc +6 -0
  164. data/lib/starter_web/pages/public/tools/previewer/preview_bootstrap_theme.adoc +17 -3
  165. data/lib/starter_web/utilsrv/_defaults/package.json +1 -1
  166. data/lib/starter_web/utilsrv/package.json +1 -1
  167. metadata +38 -7
  168. data/lib/starter_web/_includes/google/static/google_ad_7284712660.html +0 -19
@@ -0,0 +1,947 @@
1
+ /*
2
+ # -----------------------------------------------------------------------------
3
+ # ~/assets/themes/j1/modules/speak2me/js/speak2me.js
4
+ # speak2me v.1.0 implementation (based on Articulate.js) for J1 Theme
5
+ #
6
+ # Product/Info:
7
+ # https://jekyll.one
8
+ # https://github.com/acoti/articulate.js/tree/master
9
+ #
10
+ # Copyright (C) 2023 Juergen Adams
11
+ # Copyright (C) 2017 Adam Coti
12
+ #
13
+ # J1 Theme is licensed under the MIT License.
14
+ # See: https://github.com/jekyll-one-org/j1-template/blob/main/LICENSE.md
15
+ # Articulate is licensed under the MIT License.
16
+ # See: https://github.com/acoti/articulate.js/blob/master/LICENSE
17
+ # -----------------------------------------------------------------------------
18
+ */
19
+
20
+ /* Articulate.js (1.1.0). (C) 2017 Adam Coti.
21
+ MIT @license: en.wikipedia.org/wiki/MIT_License
22
+ See Github page at: https://github.com/acoti/articulate.js
23
+ See Web site at: http://articulate.purefreedom.com
24
+ */
25
+
26
+ (function($) {
27
+ 'use strict';
28
+
29
+ var ignoreTagsUser = new Array();
30
+ var recognizeTagsUser = new Array();
31
+ var replacements = new Array();
32
+ var customTags = new Array();
33
+ var voices = new Array();
34
+
35
+ var rateDefault = 0.9;
36
+ var pitchDefault = 1;
37
+ var volumeDefault = 0.9;
38
+ var rate = rateDefault;
39
+ var pitch = pitchDefault;
40
+ var volume = volumeDefault;
41
+
42
+ var currentTranslation = getCookie('googtrans');
43
+
44
+ var voiceUserDefault = 'Google UK English Female';
45
+ var voiceChromeDefault = 'Google US English';
46
+ var ignoreProvider = 'Microsoft';
47
+ var preferredVoice = 'Natural';
48
+
49
+ var voiceLanguageGoogleDefault = {
50
+ 'de-DE': 'Google Deutsch',
51
+ 'en-US': 'Google US English',
52
+ 'en-GB': 'Google UK English Female',
53
+ 'es-ES': 'Google español',
54
+ 'fr-FR': 'Google français',
55
+ // 'hi-IN': 'Google हिन्दी',
56
+ // 'id-ID': 'Google Bahasa Indonesia',
57
+ 'it-IT': 'Google italiano',
58
+ // 'jp-JP': 'Google 日本語',
59
+ // 'ko-KR': 'Google 한국의',
60
+ 'nl-NL': 'Google Nederlands',
61
+ 'pl-PL': 'Google polski',
62
+ // 'pt-BR': 'Google português do Brasil',
63
+ 'pt-PT': 'Google português do Brasil',
64
+ // 'ru-RU': 'Google русский',
65
+ // 'zh-CN': 'Google 普通话(中国大陆)',
66
+ };
67
+
68
+ var voiceLanguageMicrosoftDefault = {
69
+ 'sq-AL': 'Microsoft Anila Online (Natural) - Albanian (Albania)',
70
+ 'ar-EG': 'Microsoft Salma Online (Natural) - Arabic (Egypt)',
71
+ 'bg-BG': 'Microsoft Kalina Online (Natural) - Bulgarian (Bulgaria)',
72
+ 'zh-CN': 'Microsoft Xiaoxiao Online (Natural) - Chinese (Mainland)',
73
+ 'hr-HR': 'Microsoft Gabrijela Online (Natural) - Croatian (Croatia)',
74
+ 'cs-CZ': 'Microsoft Antonin Online (Natural) - Czech (Czech)',
75
+ 'da-DK': 'Microsoft Christel Online (Natural) - Danish (Denmark)',
76
+ 'nl-NL': 'Microsoft Colette Online (Natural) - Dutch (Netherlands)',
77
+ 'en-GB': 'Microsoft Libby Online (Natural) - English (United Kingdom)',
78
+ 'en-US': 'Microsoft Aria Online (Natural) - English (United States)',
79
+ 'et-EE': 'Microsoft Anu Online (Natural) - Estonian (Estonia)',
80
+ 'fi-FI': 'Microsoft Noora Online (Natural) - Finnish (Finland)',
81
+ 'fr-FR': 'Microsoft Denise Online (Natural) - French (France)',
82
+ 'ka-GE': 'Microsoft Giorgi Online (Natural) - Georgian (Georgia)',
83
+ 'de-DE': 'Microsoft Katja Online (Natural) - German (Germany)',
84
+ 'el-GR': 'Microsoft Athina Online (Natural) - Greek (Greece)',
85
+ 'he-IL': 'Microsoft Avri Online (Natural) - Hebrew (Israel)',
86
+ 'hi-IN': 'Microsoft Madhur Online (Natural) - Hindi (India)',
87
+ 'hu-HU': 'Microsoft Noemi Online (Natural) - Hungarian (Hungary)',
88
+ 'it-IT': 'Microsoft Elsa Online (Natural) - Italian (Italy)',
89
+ 'ja-JP': 'Microsoft Nanami Online (Natural) - Japanese (Japan)',
90
+ }
91
+
92
+ var isEdge = /Edg/i.test( navigator.userAgent );
93
+ var chrome = /chrome/i.test( navigator.userAgent );
94
+ var isChrome = ((chrome) && (!isEdge));
95
+
96
+ var rateUserDefault;
97
+ var pitchUserDefault;
98
+ var volumeUserDefault;
99
+ var currentLanguage;
100
+ var voiceLanguageDefault;
101
+ var chunkCounter = 0;
102
+ var chunkCounterMax;
103
+ var userStoppedSpeaking = false;
104
+ var currentSection;
105
+ // var chunksEnd = false;
106
+
107
+
108
+ // -------------------------------------------------------------------------
109
+ // Internal functions
110
+ // -------------------------------------------------------------------------
111
+ //
112
+ function getCookie(name) {
113
+ var nameEQ = name + '=';
114
+ var ca = document.cookie.split(';');
115
+ for (var i = 0; i < ca.length; i++) {
116
+ var c = ca[i];
117
+ while (c.charAt(0) === ' ') {
118
+ c = c.substring(1, c.length);
119
+ }
120
+ if (c.indexOf(nameEQ) === 0) {
121
+ var value = c.substring(nameEQ.length, c.length);
122
+ return value;
123
+ }
124
+ }
125
+ return undefined;
126
+ }
127
+
128
+ function voiceTag(prepend,append) {
129
+ this.prepend = prepend;
130
+ this.append = append;
131
+ }
132
+
133
+ function voiceObj(name,language) {
134
+ this.name = name;
135
+ this.language = language;
136
+ }
137
+
138
+ // This populates the "voices" array with objects that represent the
139
+ // available voices in the current browser. Each object has two
140
+ // properties: name and language. It is loaded asynchronously in
141
+ // deference to Chrome.
142
+ //
143
+ function populateVoiceList() {
144
+ var systemVoices = speechSynthesis.getVoices();
145
+ for(var i = 0; i < systemVoices.length ; i++) {
146
+ voices.push(new voiceObj(systemVoices[i].name,systemVoices[i].lang));
147
+ }
148
+ }
149
+
150
+ populateVoiceList();
151
+ if (typeof speechSynthesis !== 'undefined' && speechSynthesis.onvoiceschanged !== undefined) {
152
+ speechSynthesis.onvoiceschanged = populateVoiceList;
153
+ }
154
+
155
+ // After checking for compatability, define the utterance object and
156
+ // then cancel the speech immediately even though nothing is yet spoken.
157
+ // This is to fix a quirk in Windows Chrome.
158
+ //
159
+ if ('speechSynthesis' in window) {
160
+ var speech = new SpeechSynthesisUtterance();
161
+ window.speechSynthesis.cancel();
162
+ }
163
+
164
+ if ( currentTranslation === undefined ) {
165
+ currentLanguage = 'en-US'
166
+ } else {
167
+ var translation = currentTranslation.split('/');
168
+ if ( translation[2] == 'en') {
169
+ currentLanguage = 'en-GB';
170
+ } else if ( translation[2].includes('ar') ) {
171
+ currentLanguage = 'ar-EG';
172
+ } else if ( translation[2].includes('cs') ) {
173
+ currentLanguage = 'cs-CZ';
174
+ } else if ( translation[2].includes('da') ) {
175
+ currentLanguage = 'da-DK';
176
+ } else if ( translation[2].includes('en') ) {
177
+ currentLanguage = 'en-UK';
178
+ } else if ( translation[2].includes('et') ) {
179
+ currentLanguage = 'et-EE';
180
+ } else if ( translation[2].includes('ka') ) {
181
+ currentLanguage = 'ka-GE';
182
+ } else if ( translation[2].includes('el') ) {
183
+ currentLanguage = 'el-GR';
184
+ } else if ( translation[2].includes('iw') ) {
185
+ currentLanguage = 'he-IL';
186
+ } else if ( translation[2].includes('hi') ) {
187
+ currentLanguage = 'hi-IN';
188
+ } else if ( translation[2].includes('ja') ) {
189
+ currentLanguage = 'ja-JP';
190
+ } else if ( translation[2].includes('zh') ) {
191
+ currentLanguage = 'zh-CN';
192
+ } else {
193
+ currentLanguage = translation[2] + '-' + translation[2].toUpperCase();
194
+ }
195
+ }
196
+
197
+ if (isChrome) {
198
+ var voiceLanguageDefault = voiceLanguageGoogleDefault[currentLanguage];
199
+ }
200
+
201
+ if (isEdge) {
202
+ var voiceLanguageDefault = voiceLanguageMicrosoftDefault[currentLanguage];
203
+ }
204
+
205
+ // -------------------------------------------------------------------------
206
+ // Public functions
207
+ // -------------------------------------------------------------------------
208
+ //
209
+ var methods = {
210
+
211
+ speak: function(options) {
212
+ var opts = $.extend( {}, $.fn.speak2me.defaults, options );
213
+ var toSpeak = '';
214
+ var obj, processed, finished;
215
+ var voiceTags = new Array();
216
+ var ignoreTags;
217
+ // var chunksEnd = false;
218
+
219
+ // Default values
220
+ //
221
+ voiceTags['a'] = new voiceTag('clicking the link,', '');
222
+ voiceTags['q'] = new voiceTag(',', '');
223
+ voiceTags['ol'] = new voiceTag('Start of list.', 'End of list.');
224
+ voiceTags['ul'] = new voiceTag('Start of list.', 'End of list.');
225
+ // voiceTags["li"] = new voiceTag(',', ',');
226
+ voiceTags['dl'] = new voiceTag('Start of list.', 'End of list.');
227
+ voiceTags['dt'] = new voiceTag('', ', ');
228
+ voiceTags['img'] = new voiceTag('There\'s an embedded image with the description,', '');
229
+ voiceTags['table'] = new voiceTag('There\'s an embedded table with the caption,', 'Note, table contents are not spoken, ');
230
+ // voiceTags["tbody"] = new voiceTag(', ', ',');
231
+ voiceTags['figure'] = new voiceTag('There\'s an embedded figure with the caption,', '');
232
+ voiceTags['blockquote'] = new voiceTag('Blockquote start.', 'Blockquote end.');
233
+
234
+ // ignoreTags = ['audio','button','canvas','code','del','dialog','dl','embed','form','head','iframe','meter','nav','noscript','object','s','script','select','style','textarea','video'];
235
+ ignoreTags = ['audio','button','canvas','code','del','dialog','embed','form','head','iframe','meter','nav','noscript','object','s','script','select','style','textarea','video'];
236
+
237
+ // Check to see if the browser supports the functionality.
238
+ //
239
+ if (!('speechSynthesis' in window)) {
240
+ alert('Sorry, this browser does not support the Web Speech API.');
241
+ return
242
+ };
243
+
244
+ // If something is currently being spoken, ignore new voice
245
+ // request. Otherwise it would be queued, which is doable if
246
+ // someone wanted that, but not what I wanted.
247
+ //
248
+ if (window.speechSynthesis.speaking) {
249
+ return
250
+ };
251
+
252
+ // Cycle through all the elements in the original jQuery
253
+ // selector, process and clean them one at a time, and
254
+ // continually append it to the variable "toSpeak".
255
+ //
256
+ this.each(function() {
257
+ obj = $(this).clone(); // clone the DOM node and its descendants of what the user wants spoken
258
+ processed = processDOMelements(obj); // process and manipulate DOM tree of this clone
259
+ processed = jQuery(processed).html(); // convert the result of all that to a string
260
+ finished = cleanDOMelements(processed); // do some text manipulation
261
+ // toSpeak = toSpeak + ' ' + finished; // add it to what will ultimately be spoken after cycling through selectors
262
+ toSpeak = finished;
263
+ });
264
+
265
+ // Check if users have set their own rate/pitch/volume
266
+ // defaults, otherwise use defaults
267
+ //
268
+ if (rateUserDefault !== undefined) {
269
+ rate = rateUserDefault;
270
+ } else {
271
+ rate = rateDefault;
272
+ }
273
+ if (pitchUserDefault !== undefined) {
274
+ pitch = pitchUserDefault;
275
+ } else {
276
+ pitch = pitchDefault;
277
+ }
278
+ if (volumeUserDefault !== undefined) {
279
+ volume = volumeUserDefault;
280
+ } else {
281
+ volume = volumeDefault;
282
+ }
283
+
284
+ // jadams
285
+ // To debug, uncomment the following to see exactly what's
286
+ // about to be spoken.
287
+ // console.log(toSpeak)
288
+
289
+ // jadams
290
+ // This is where the magic happens. Well, not magic, but at
291
+ // least we can finally hear something. After the line that
292
+ // fixes the Windows Chrome quirk, the custom voice is set
293
+ // if one has been chosen.
294
+ //
295
+ speech = new SpeechSynthesisUtterance();
296
+ // speech.text = toSpeak;
297
+ speech.rate = rate;
298
+ speech.pitch = pitch;
299
+ speech.volume = volume;
300
+ speech.voice = undefined;
301
+
302
+ // jadams
303
+ if (isChrome) {
304
+ speech.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == voiceLanguageDefault; })[0];
305
+ // speech.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == voiceChromeDefault; })[0];
306
+ };
307
+
308
+ // jadams
309
+ if (isEdge) {
310
+ speech.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == voiceLanguageDefault; })[0];
311
+ };
312
+
313
+ // jadams
314
+ processTextChunks(speech, toSpeak);
315
+
316
+ // jadams
317
+ function splitTextIntoChunks(text, chunkSize) {
318
+ const chunks = text.split(/[.!?]/);
319
+
320
+ // cleanup chunks
321
+ //
322
+ chunks.forEach((chunk, index) => {
323
+ chunks[index] = chunk.replace(/^\s+|\s+$/g, '');
324
+ chunks[index] = chunks[index] + '. ';
325
+ });
326
+
327
+ return chunks;
328
+ }
329
+
330
+ // jadams
331
+ // function splitSectionIntoSentences(section) {
332
+ // const chunks = section.innerHTML.split(/[.!?]/);
333
+ //
334
+ // // cleanup chunks
335
+ // //
336
+ // chunks.forEach((chunk, index) => {
337
+ // if (chunks[index] !== '') {
338
+ // chunks[index] = chunks[index] + '.';
339
+ // // chunks[index] = chunks[index].wrap('<span class="new"/>');
340
+ // }
341
+ //
342
+ // // chunks[index] = chunk.replace(/^\s+|\s+$/g, '');
343
+ // // chunks[index] = chunk.replace(/^\s+|\s+$/g, '');
344
+ // });
345
+ //
346
+ // return chunks;
347
+ // }
348
+
349
+ // jadams
350
+ // Funktion zur sequenziellen Verarbeitung der Textkette
351
+ //
352
+ function processTextChunks(speaker, chunks) {
353
+ const synth = window.speechSynthesis;
354
+
355
+ speaker.text = chunks[chunkCounter];
356
+ synth.speak(speaker);
357
+
358
+ speaker.addEventListener('start', (event) => {
359
+ // console.log('speak2me start:', event);
360
+ $('.mdib-speaker').addClass('mdib-spin');
361
+ });
362
+
363
+ speaker.addEventListener('end', function (event) {
364
+ chunkCounter ++;
365
+ // console.log('speak2me end:', chunkCounter);
366
+ $(currentSection).removeClass('speak-highlighted');
367
+ });
368
+
369
+ // jadams
370
+ var speechMonitor = setInterval(function () {
371
+ // chunkCounterMax = 4;
372
+ if (! synth.speaking ) {
373
+ if (chunkCounter == chunkCounterMax || userStoppedSpeaking ) {
374
+ chunkCounter = 0;
375
+ userStoppedSpeaking = false;
376
+
377
+ synth.cancel();
378
+ $('.mdib-speaker').removeClass('mdib-spin');
379
+ clearInterval(speechMonitor);
380
+ } else {
381
+ speaker.text = chunks[chunkCounter];
382
+ var sectionText = speaker.text.substr(0, 20);
383
+
384
+ currentSection = $('#content').find("p:contains('" + sectionText + "')")[0];
385
+ $(currentSection).addClass('speak-highlighted');
386
+
387
+ synth.speak(speaker);
388
+ }
389
+ }
390
+ }, 500); // END speechMonitor
391
+
392
+ } // END processTextChunks
393
+
394
+ // jadams
395
+ function processDOMelements(clone) {
396
+ var copy, content, appended, prepend;
397
+
398
+ // Remove tags from the "ignoreTags" array because the
399
+ // user called "speak2me('recognize')" and said he/she
400
+ // doesn't want some tags un-spoken. Double negative there,
401
+ // but it does make sense.
402
+ //
403
+ if (recognizeTagsUser.length > 0) {
404
+ for (var prop in recognizeTagsUser) {
405
+ var index = ignoreTags.indexOf(recognizeTagsUser[prop]);
406
+ if (index > -1) {
407
+ ignoreTags.splice(index, 1);
408
+ }
409
+ };
410
+ };
411
+
412
+ // Remove DOM objects from those listed in the "ignoreTags"
413
+ // array now that the user has specified which ones,
414
+ // if any, he/she wants to keep
415
+ //
416
+ for (var prop in ignoreTags) {
417
+ jQuery(clone).find(ignoreTags[prop]).addBack(ignoreTags[prop]).not('[data-speak2me-recognize]').each(function() {
418
+ jQuery(this).html('');
419
+ });
420
+ };
421
+
422
+ // Remove DOM objects specified in the "ignoreTagsUser"
423
+ // array that the user specified when calling "speak2me('ignore')".
424
+ //
425
+ if (ignoreTagsUser.length > 0) {
426
+ for (var prop in ignoreTagsUser) {
427
+ jQuery(clone).find(ignoreTagsUser[prop]).addBack(ignoreTagsUser[prop]).not('[data-speak2me-recognize]').each(function() {
428
+ jQuery(this).html('');
429
+ });
430
+ };
431
+ };
432
+
433
+ // Remove DOM objects specified in the HTML with
434
+ // "data-speak2me-ignore"
435
+ //
436
+ jQuery(clone).find('[data-speak2me-ignore]').addBack('[data-speak2me-ignore]').each(function() {
437
+ jQuery(this).html('');
438
+ });
439
+
440
+ // jadams
441
+ // Remove DOM objects specified in the HTML by class "speak2me-ignore"
442
+ //
443
+ jQuery(clone).find('.speak2me-ignore').addBack('[data-speak2me-ignore]').each(function() {
444
+ // jQuery(this).html("The following element in the article is skipped to be spoken. ");
445
+ jQuery(this).html('');
446
+ });
447
+
448
+ // Search for prepend data specified in the HTML
449
+ // with "data-speak2me-prepend"
450
+ jQuery(clone).find('[data-speak2me-prepend]').addBack('[data-speak2me-prepend]').each(function() {
451
+ copy = jQuery(this).data('speak2me-prepend');
452
+ jQuery(this).prepend(copy + ' ');
453
+ });
454
+
455
+ // Search for append data specified in the HTML
456
+ // with "data-speak2me-append"
457
+ //
458
+ jQuery(clone).find('[data-speak2me-append]').addBack('[data-speak2me-append]').each(function() {
459
+ copy = jQuery(this).data('speak2me-append');
460
+ jQuery(this).append(' ' + copy);
461
+ });
462
+
463
+ // jadams
464
+ // Search for tags to prepend and append specified by
465
+ // the "voiceTags" array.
466
+ //
467
+ var count = 0;
468
+ for (var tag in voiceTags) {
469
+ // count++
470
+ // if (count <= 4) {
471
+ jQuery(clone).find(tag).each(function() {
472
+ if (customTags[tag]) {
473
+ jQuery(this).prepend(customTags[tag].prepend + ' ');
474
+ jQuery(this).append(' ' + customTags[tag].append);
475
+ } else {
476
+ jQuery(this).prepend(voiceTags[tag].prepend + ' ');
477
+ jQuery(this).append(' ' + voiceTags[tag].append);
478
+ };
479
+ });
480
+ // };
481
+ };
482
+
483
+ // Search for <h1> through <h6> and <li> and <br> to add
484
+ // a pause at the end of those tags. This is done
485
+ // because these tags require a pause, but often don't
486
+ // have a comma or period at the end of their text.
487
+ //
488
+ jQuery(clone).find('h1,h2,h3,h4,h5,h6,li,p').addBack('h1,h2,h3,h4,h5,h6,li,p').each(function() {
489
+ jQuery(this).append('. ');
490
+ });
491
+ jQuery(clone).find('br').each(function() {
492
+ jQuery(this).after(', ');
493
+ });
494
+
495
+ // Search for <figure>, check for <figcaption>, insert
496
+ // that text if it exists and then remove the whole DOM object
497
+ //
498
+ jQuery(clone).find('figure').addBack('figure').each(function() {
499
+ copy = jQuery(this).find('figcaption').html();
500
+ if (customTags['figure']) {
501
+ prepend = customTags['figure'].prepend
502
+ }
503
+ else {
504
+ prepend = voiceTags['figure'].prepend
505
+ }
506
+ if ((copy != undefined) && (copy !== '')) {
507
+ jQuery('<div>' + prepend + ' ' + copy + '.</div>').insertBefore(this);
508
+ }
509
+ jQuery(this).remove();
510
+ });
511
+
512
+ // Search for <image>, check for ALT attribute, insert that
513
+ // text if it exists and then remove the whole DOM object.
514
+ // Had to make adjustments for nesting in <picture> tags.
515
+ //
516
+ jQuery(clone).find('img').addBack('img').each(function() {
517
+ copy = jQuery(this).attr('alt');
518
+ var parent = jQuery(this).parent();
519
+ var parentName = parent.get(0).tagName;
520
+ if (customTags['img']) {
521
+ prepend = customTags['img'].prepend
522
+ }
523
+ else {
524
+ prepend = voiceTags['img'].prepend
525
+ };
526
+ if ((copy !== undefined) && (copy != '')) {
527
+ if (parentName == 'PICTURE') {
528
+ var par
529
+ jQuery('<div>' + prepend + ' ' + copy + '.</div>').insertBefore(parent);
530
+ } else {
531
+ jQuery('<div>' + prepend + ' ' + copy + '.</div>').insertBefore(this);
532
+ }
533
+ };
534
+ jQuery(this).remove();
535
+ });
536
+
537
+ // jadams
538
+ // Search for <table>, check for <caption>, insert that
539
+ // text if it exists and then remove the whole DOM object
540
+ //
541
+ jQuery(clone).find('table').addBack('table').each(function() {
542
+ copy = jQuery(this).find('caption').text();
543
+ // content = jQuery(this).find("tbody");
544
+
545
+ if (customTags['table']) {
546
+ prepend = customTags['table'].prepend
547
+ appended = customTags['table'].append;
548
+ }
549
+ else {
550
+ prepend = voiceTags['table'].prepend
551
+ appended = voiceTags['table'].append;
552
+ }
553
+
554
+ if ((copy !== undefined) && (copy != '')) {
555
+ jQuery('<div>' + prepend + ' ' + copy + '.</div>').insertBefore(this);
556
+ jQuery('<div>' + appended + ' ' + '.</div>').insertBefore(this);
557
+ }
558
+
559
+ // TODO: processing the table contents
560
+ //
561
+ // if ((content !== undefined) && (content != '')) {
562
+ // jQuery('<div>' + ' ' + content + "</div>").insertBefore(this);
563
+ // }
564
+
565
+ jQuery(this).remove();
566
+ });
567
+
568
+ // Search for DOM object to be replaced specified in
569
+ // the HTML with "data-speak2me-swap"
570
+ //
571
+ jQuery(clone).find('[data-speak2me-swap]').addBack('[data-speak2me-swap]').each(function() {
572
+ copy = jQuery(this).data('speak2me-swap');
573
+ jQuery(this).text(copy);
574
+ });
575
+
576
+ // Search for DOM object to spelled out specified in
577
+ // the HTML with "data-speak2me-spell".
578
+ // I find this function fun if, admittedly, not too practical.
579
+ //
580
+ jQuery(clone).find('[data-speak2me-spell]').addBack('[data-speak2me-spell]').each(function() {
581
+ copy = jQuery(this).text();
582
+ copy = copy.split('').join(' ');
583
+ jQuery(this).text(copy);
584
+ });
585
+ return clone;
586
+ }
587
+
588
+ // jadams
589
+ function cleanDOMelements(final) {
590
+ var start,ended,speak,part1,part2,final;
591
+
592
+ // Search for <speak2me> in comments, copy the text,
593
+ // place it outside the comment, and then splice together
594
+ // "final" string again, which omits the comment.
595
+ //
596
+ while (final.indexOf('<!-- <speak2me>') != -1) {
597
+ start = final.indexOf('<!-- <speak2me>');
598
+ ended = final.indexOf('</speak2me> -->', start);
599
+ if (ended == -1) { break; }
600
+ speak = final.substring(start + 17, ended);
601
+ part1 = final.substring(0, start);
602
+ part2 = final.substring(ended + 17);
603
+ final = part1 + ' ' + speak + ' ' + part2;
604
+ };
605
+
606
+ // Strip out remaining comments.
607
+ //
608
+ final = final.replace(/<!--[\s\S]*?-->/g, '');
609
+
610
+ // Strip out remaining HTML tags
611
+ //
612
+ final = final.replace(/(<([^>]+)>)/ig, '');
613
+
614
+ // Replace a string of characters with another as specified
615
+ // by "speak2me('replace')".
616
+ //
617
+ var len = replacements.length;
618
+ var i = 0;
619
+ var old, rep;
620
+ while (i < len) {
621
+ old = replacements[i];
622
+ old = old.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
623
+ rep = replacements[i + 1] + ' ';
624
+ var regexp = new RegExp(old, 'gi');
625
+ var final = final.replace(regexp, rep);
626
+ i = i + 2;
627
+ };
628
+
629
+ // Replace double smart quotes with proper text, same as what
630
+ // was done previously with <q>
631
+ //
632
+ if (customTags['q']) {
633
+ final = final.replace(/“/g, customTags['q'].prepend + ' ');
634
+ final = final.replace(/”/g, ' ' + customTags['q'].append);
635
+ } else {
636
+ final = final.replace(/“/g, voiceTags['q'].prepend + ' ');
637
+ final = final.replace(/”/g, ' ' + voiceTags['q'].append);
638
+ }
639
+
640
+ // jadams
641
+ // Replace colons ':' with a pause since
642
+ // the browser doesn't do so when reading.
643
+ //
644
+ final = final.replace(/:/g, ', ');
645
+
646
+ // jadams
647
+ // Replace empty lines
648
+ //
649
+ final = final.replace(/^$/g, '\n');
650
+ final = final.replace(/^\s+$/g, '\n');
651
+
652
+ // jadams
653
+ // Replace single full stops in line ' . ' or '. '
654
+ //
655
+ final = final.replace(/\s+\.\s+/g, '\n');
656
+ final = final.replace(/\s+\.\s+$/g, '\n');
657
+
658
+ // jadams
659
+ // Replace strange double full stops '..'
660
+ //
661
+ final = final.replace(/\.\./g, '.');
662
+
663
+ // jadams
664
+ // Replace the abbreviation '.e.g.'or ',.e.g.,'
665
+ //
666
+ final = final.replace(/\se\.g\.\s/g, ' for example, ');
667
+ final = final.replace(/\,\s+\.g\.\s+\,/g, ' for example, ');
668
+
669
+ // jadams
670
+ // Replace question and exclamation (?|!) marks
671
+ //
672
+ final = final.replace(/[\!\?]/g, ', ');
673
+
674
+ // Replace em-dashes and double-dashes with a pause since
675
+ // the browser doesn't do so when reading.
676
+ //
677
+ final = final.replace(/—/g, ', ');
678
+ final = final.replace(/--/g, ', ');
679
+
680
+ // When read from the DOM, a few special characters
681
+ // (&amp; for example) display as their hex codes
682
+ // rather than resolving into their actual character
683
+ //
684
+ var txt = document.createElement('textarea');
685
+ txt.innerHTML = final;
686
+ final = txt.value;
687
+
688
+ // Strip out new line characters and carriage returns,
689
+ // which cause unwanted pauses.
690
+ //
691
+ final = final.replace(/(\r\n|\n|\r)/gm, '');
692
+
693
+ // Strip out multiple spaces and periods and commas
694
+ // for neatness more than anything else since
695
+ // none of this will affect the speech. But it helps when
696
+ // checking progress in the console.
697
+ //
698
+ final = final.replace(/ +/g, ' ');
699
+ final = final.replace(/\.\./g, '.');
700
+ final = final.replace(/,,/g, ',');
701
+ final = final.replace(/ ,/g, ',');
702
+ final = final.replace(/^\s+|\s+$/g, '');
703
+
704
+ // final = final.replace(/^\s+|\s+$/g,'\n');
705
+ // final = final.replace(/\. /g, '. \n');
706
+
707
+ // Definiere die Größe der Abschnitte
708
+ const chunkSize = 100;
709
+
710
+ // Teile den Text in Abschnitte auf
711
+ const textChunks = splitTextIntoChunks(final, chunkSize);
712
+ chunkCounterMax = textChunks.length;
713
+
714
+ // return final;
715
+ return textChunks;
716
+ }
717
+
718
+ // return the SpeechSynthesisUtterance object to be used
719
+ return speech;
720
+ },
721
+
722
+ pause: function() {
723
+ window.speechSynthesis.pause();
724
+ return this;
725
+ },
726
+
727
+ resume: function() {
728
+ window.speechSynthesis.resume();
729
+ return this;
730
+ },
731
+
732
+ // jadams
733
+ stop: function() {
734
+ window.speechSynthesis.cancel();
735
+ userStoppedSpeaking = true;
736
+ return this;
737
+ },
738
+
739
+ enabled: function() {
740
+ return ('speechSynthesis' in window);
741
+ },
742
+
743
+ isSpeaking: function() {
744
+ return (window.speechSynthesis.speaking);
745
+ },
746
+
747
+ isPaused: function() {
748
+ return (window.speechSynthesis.paused);
749
+ },
750
+
751
+ rate: function() {
752
+ var num = arguments[0];
753
+ if ((num >= 0.1) && (num <= 10)) {
754
+ rateUserDefault = num;
755
+ } else if (num === undefined) {
756
+ rateUserDefault = void 0;
757
+ rate = rateDefault;
758
+ };
759
+ return this;
760
+ },
761
+
762
+ pitch: function() {
763
+ var num = arguments[0];
764
+ if ((num >= 0.1) && (num <= 2)) {
765
+ pitchUserDefault = num;
766
+ } else if (num === undefined) {
767
+ pitchUserDefault = void 0;
768
+ pitch = pitchDefault;
769
+ };
770
+ return this;
771
+ },
772
+
773
+ volume: function() {
774
+ var num = arguments[0];
775
+ if ((num >= 0) && (num <= 1)) {
776
+ volumeUserDefault = num;
777
+ } else if (num === undefined) {
778
+ volumeUserDefault = void 0;
779
+ volume = volumeDefault;
780
+ };
781
+ return this;
782
+ },
783
+
784
+ ignore: function() {
785
+ var len = arguments.length;
786
+ ignoreTagsUser.length = 0;
787
+ while (len > 0) {
788
+ len--;
789
+ ignoreTagsUser.push(arguments[len]);
790
+ };
791
+ return this;
792
+ },
793
+
794
+ recognize: function() {
795
+ var len = arguments.length;
796
+ recognizeTagsUser.length = 0;
797
+ while (len > 0) {
798
+ len--;
799
+ recognizeTagsUser.push(arguments[len]);
800
+ };
801
+ return this;
802
+ },
803
+
804
+ replace: function() {
805
+ var len = arguments.length;
806
+ replacements.length = 0;
807
+ var i = 0;
808
+ while (i < len) {
809
+ replacements.push(arguments[i], arguments[i + 1]);
810
+ i = i + 2;
811
+ if ((len - i) == 1) { break; };
812
+ };
813
+ return this;
814
+ },
815
+
816
+ customize: function() {
817
+ var len = arguments.length;
818
+ if (len == 0) {
819
+ customTags = [];
820
+ };
821
+ if (len == 2) {
822
+ if (['img','table','figure'].indexOf(arguments[0]) == -1) { console.log("Error: When customizing, tag indicated must be either 'img', 'table', or 'figure'."); return; }
823
+ customTags[arguments[0].toString()] = new voiceTag(arguments[1].toString());
824
+ };
825
+ if (len == 3) {
826
+ if (['q',"ol","ul","blockquote"].indexOf(arguments[0]) == -1) { console.log("Error: When customizing, tag indicated must be either 'q', 'ol', 'ul' or 'blockquote'."); return; }
827
+ customTags[arguments[0].toString()] = new voiceTag(arguments[1].toString(), arguments[2].toString());
828
+ };
829
+ return this;
830
+ },
831
+
832
+ getVoices: function() {
833
+ // If no arguments, then the user has requested the array of
834
+ // voices populated earlier.
835
+ //
836
+ if (arguments.length == 0) {
837
+ return voices;
838
+ };
839
+ // If there's another argument, we'll assume it's a jQuery
840
+ // selector designating where to put the dropdown menu.
841
+ // And if there's a third argument, that will be custom text
842
+ // for the dropdown menu.
843
+ // Then we'll create a dropdown menu with the voice names and,
844
+ //in parenthesis, the language code.
845
+ //
846
+ var obj = jQuery(arguments[0]);
847
+ var selectionTxt = 'Choose a voice';
848
+
849
+ if (arguments[1] !== undefined) {
850
+ selectionTxt = arguments[1];
851
+ }
852
+
853
+ obj.append(jQuery("<select id='voiceSelect' name='voiceSelect'><option value='none'>" + selectionTxt + "</option></select>"));
854
+
855
+ // jadams
856
+ var skippedVoices = 0;
857
+ for (var i = 0; i < voices.length ; i++) {
858
+ if (isChrome && voices[i].name.includes(ignoreProvider)) {
859
+ skippedVoices++;
860
+ continue;
861
+ }
862
+ if (isEdge && !voices[i].name.includes('Natural')) {
863
+ skippedVoices++;
864
+ continue;
865
+ }
866
+ var option = document.createElement('option');
867
+ option.textContent = voices[i].name + ' (' + voices[i].language + ')';
868
+ option.setAttribute('value', voices[i].name);
869
+
870
+ // set used voice as 'selected'
871
+ if (voiceLanguageDefault !== undefined) {
872
+ if ( voices[i].name === voiceLanguageDefault ) {
873
+ option.setAttribute('selected', 'selected');
874
+ }
875
+ } else {
876
+ if ( voices[i].name.includes(voiceUserDefault) ) {
877
+ // option.setAttribute('selected', 'selected');
878
+ }
879
+ }
880
+
881
+ option.setAttribute('data-speak2me-language', voices[i].language);
882
+ obj.find('select').append(option);
883
+ }
884
+
885
+ // jadams
886
+ return i - skippedVoices;
887
+ },
888
+
889
+ setVoice: function() {
890
+ // The setVoice function has to have two attributes
891
+ // if not, exit the function.
892
+ //
893
+ if (arguments.length < 2) {
894
+ return this
895
+ }
896
+ var requestedVoice, requestedLanguage;
897
+
898
+ // User wants to change the voice directly. If that name indeed
899
+ // exists, update the "voiceUserDefault" variable.
900
+ //
901
+ if (arguments[0] == 'name') {
902
+ requestedVoice = arguments[1];
903
+ for (var i = 0; i < voices.length; i++) {
904
+ if (voices[i].name == requestedVoice) {
905
+ voiceUserDefault = requestedVoice;
906
+ };
907
+ };
908
+ };
909
+
910
+ // User wants to change the voice by only specifying the
911
+ // first two characters of the language code. Case insensitive.
912
+ //
913
+ if (arguments[0] == 'language') {
914
+ requestedLanguage = arguments[1].toUpperCase();
915
+ if (requestedLanguage.length == 2) {
916
+ for (var i = 0; i < voices.length; i++) {
917
+ if (voices[i].language.substring(0,2).toUpperCase() == requestedLanguage) {
918
+ voiceUserDefault = voices[i].name;
919
+ break
920
+ };
921
+ };
922
+ } else {
923
+ // User wants to change the voice by specifying the
924
+ // complete language code.
925
+ for (var i = 0; i < voices.length; i++) {
926
+ if (voices[i].language == requestedLanguage) {
927
+ voiceUserDefault = voices[i].name;
928
+ break
929
+ };
930
+ };
931
+ }
932
+ };
933
+ return this;
934
+ },
935
+ };
936
+
937
+ $.fn.speak2me = function(method) {
938
+ if (methods[method]) {
939
+ return methods[method].apply( this, Array.prototype.slice.call(arguments, 1));
940
+ } else if (typeof method === 'object' || ! method) {
941
+ return methods.speak.apply(this, arguments);
942
+ } else {
943
+ jQuery.error('Method ' + method + ' does not exist on jQuery.speak2me');
944
+ }
945
+ };
946
+
947
+ }(jQuery));