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.
- checksums.yaml +4 -4
- data/_includes/themes/j1/layouts/content_generator_news_panel_posts.html +11 -9
- data/_includes/themes/j1/layouts/content_generator_post.html +8 -6
- data/_includes/themes/j1/procedures/global/create_word_cloud.proc +1 -1
- data/_includes/themes/j1/procedures/layouts/module_writer.proc +1 -1
- data/_includes/themes/j1/procedures/posts/collate_timeline.proc +4 -4
- data/_includes/themes/j1/procedures/posts/pager.proc +1 -1
- data/_layouts/_home.html +88 -0
- data/_layouts/home.html +1 -1
- data/assets/data/banner.html +7 -7
- data/assets/data/panel.html +37 -25
- data/assets/data/quicklinks.html +40 -30
- data/assets/data/speak2me.html +219 -0
- data/assets/data/translator.html +32 -4
- data/assets/themes/j1/adapter/js/speak2me.js +425 -0
- data/assets/themes/j1/adapter/js/translator.js +10 -2
- data/assets/themes/j1/core/css/icon-fonts/mdib.css +21 -0
- data/assets/themes/j1/core/css/icon-fonts/mdib.min.css +1 -1
- data/assets/themes/j1/core/css/themes/bootstrap/bootstrap.css +34 -40
- data/assets/themes/j1/core/css/themes/bootstrap/bootstrap.min.css +2 -2
- data/assets/themes/j1/core/css/themes/unodark/bootstrap.css +77 -57
- data/assets/themes/j1/core/css/themes/unodark/bootstrap.min.css +2 -2
- data/assets/themes/j1/core/css/themes/unolight/bootstrap.css +176 -42
- data/assets/themes/j1/core/css/themes/unolight/bootstrap.min.css +3 -3
- data/assets/themes/j1/core/js/template.js +1658 -0
- data/assets/themes/j1/core/js/template.min.js +11 -5
- data/assets/themes/j1/core/js/template.min.js.map +1 -1
- data/assets/themes/j1/modules/speak2me/LICENSE +21 -0
- data/assets/themes/j1/modules/speak2me/js/speak2me.js +947 -0
- data/assets/themes/j1/modules/speak2me/js/speak2me.min.js +26 -0
- data/assets/themes/j1/modules/translator/js/translator.js +30 -13
- data/assets/themes/j1/modules/translator/js/translator.min.js +1 -1
- data/lib/j1/version.rb +1 -1
- data/lib/starter_web/Gemfile +5 -5
- data/lib/starter_web/README.md +5 -5
- data/lib/starter_web/_config.yml +12 -44
- data/lib/starter_web/_data/blocks/banner.yml +7 -6
- data/lib/starter_web/_data/blocks/panel.yml +37 -39
- data/lib/starter_web/_data/layouts/home.yml +1 -1
- data/lib/starter_web/_data/modules/advertising.yml +10 -88
- data/lib/starter_web/_data/modules/buymeacoffee.yml +30 -0
- data/lib/starter_web/_data/modules/defaults/advertising.yml +3 -1
- data/lib/starter_web/_data/modules/defaults/blog_navigator.yml +97 -164
- data/lib/starter_web/_data/modules/defaults/buymeacoffee.yml +30 -0
- data/lib/starter_web/_data/modules/defaults/navigator.yml +8 -4
- data/lib/starter_web/_data/modules/defaults/speak2me.yml +72 -0
- data/lib/starter_web/_data/modules/scroller.yml +1 -1
- data/lib/starter_web/_data/modules/speak2me.yml +33 -0
- data/lib/starter_web/_data/resources.yml +24 -1
- data/lib/starter_web/_data/templates/feed.xml +1 -1
- data/lib/starter_web/_includes/google/static/{google_ad_5128488466.html → google_ad__your-slot-id.html} +1 -1
- data/lib/starter_web/_plugins/asciidoctor/callout.rb +1 -1
- data/lib/starter_web/_plugins/asciidoctor/carousel-block.rb +2 -2
- data/lib/starter_web/_plugins/asciidoctor/gallery-block.rb +2 -2
- data/lib/starter_web/_plugins/asciidoctor/gist-block.rb +2 -2
- data/lib/starter_web/_plugins/asciidoctor/google-ad-block.rb +1 -1
- data/lib/starter_web/_plugins/asciidoctor/lightbox-block.rb +2 -7
- data/lib/starter_web/_plugins/asciidoctor/masonry-block.rb +2 -3
- data/lib/starter_web/_plugins/asciidoctor/masterslider-block.rb +2 -2
- data/lib/starter_web/_plugins/asciidoctor/mdib-icon-inline.rb +43 -0
- data/lib/starter_web/_plugins/asciidoctor/mdil-icon-inline.rb +1 -1
- data/lib/starter_web/_plugins/asciidoctor/range-slider-block.rb +1 -1
- data/lib/starter_web/_plugins/asciidoctor/slick-block.rb +2 -2
- data/lib/starter_web/_plugins/asciidoctor/textbook-block.rb +1 -1
- data/lib/starter_web/_plugins/filter/liquify.rb +22 -22
- data/lib/starter_web/_plugins/index/lunr.rb +1 -1
- data/lib/starter_web/assets/images/collections/books/biography/a_life_in_questions.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/biography/becoming.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/biography/born_to_run.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/biography/forty_autumns.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/biography/not_dead_yet.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/biography/the_princess_diarist.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/biography/when_breath_becomes_air.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/fantasy/harry-potter-deathly-hallows.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/fantasy/harry-potter-philosophers-stone.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/fantasy/mistborn-trilogy.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/fantasy/ready-player-one.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/fantasy/sword-of-destiny.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/fantasy/terry-pratchet-diary.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/romance/breath-of-snow-and-ashes.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/romance/it-ends-with-us.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/romance/outlander-novel.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/romance/outlander-short-story.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/romance/the-fiery-cross.jpg +0 -0
- data/lib/starter_web/assets/images/collections/books/romance/the_dressmaker.jpg +0 -0
- data/lib/starter_web/collections/_biography/a-life-in-questions.adoc +13 -8
- data/lib/starter_web/collections/_biography/becoming.adoc +18 -12
- data/lib/starter_web/collections/_biography/born-to-run.adoc +17 -13
- data/lib/starter_web/collections/_biography/forty-autumns.adoc +14 -9
- data/lib/starter_web/collections/_biography/not-dead-yet.adoc +12 -7
- data/lib/starter_web/collections/_biography/princess-diarist-the.adoc +13 -8
- data/lib/starter_web/collections/_biography/when-breath-becomes-air.adoc +14 -9
- data/lib/starter_web/collections/_fantasy/harry-potter-deathly-hallows.adoc +13 -7
- data/lib/starter_web/collections/_fantasy/harry-potter-philosophers-stone.adoc +12 -8
- data/lib/starter_web/collections/_fantasy/mistborn-trilogy.adoc +14 -10
- data/lib/starter_web/collections/_fantasy/ready-player-one.adoc +12 -11
- data/lib/starter_web/collections/_fantasy/sword-of-destiny.adoc +15 -7
- data/lib/starter_web/collections/_fantasy/terry-pratchet-diary.adoc +11 -6
- data/lib/starter_web/collections/_romance/breath-of-snow-and-ashes.adoc +15 -10
- data/lib/starter_web/collections/_romance/it-ends-with-us.adoc +11 -6
- data/lib/starter_web/collections/_romance/outlander-novel.adoc +11 -6
- data/lib/starter_web/collections/_romance/{virgins-outlander-short-story.adoc → outlander-virgins-short-story.adoc} +11 -6
- data/lib/starter_web/collections/_romance/{dressmaker-the.adoc → the-dressmaker.adoc} +11 -6
- data/lib/starter_web/collections/_romance/{fiery-cross-the.adoc → the-fiery-cross.adoc} +11 -6
- data/lib/starter_web/collections/posts/public/featured/_posts/0000-00-00-welcome-to-j1.adoc.erb +6 -6
- data/lib/starter_web/collections/posts/public/featured/_posts/2021-01-01-about-cookies.adoc +12 -11
- data/lib/starter_web/collections/posts/public/featured/_posts/2021-02-01-static-site-generators.adoc +9 -7
- data/lib/starter_web/collections/posts/public/featured/_posts/2022-02-01-about-j1.adoc +8 -8
- data/lib/starter_web/dot.ruby-version +1 -1
- data/lib/starter_web/index.html +10 -8
- data/lib/starter_web/package.json +1 -1
- data/lib/starter_web/pages/public/about/features.adoc +7 -1
- data/lib/starter_web/pages/public/about/reporting_issues.adoc +2 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/000_intro.adoc +2 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/100_converter.adoc +2 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/200_themes.adoc +2 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/_includes/documents/000_intro.asciidoc +1 -1
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/_includes/documents/100_converter/112_getting_started.asciidoc +1 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/_includes/documents/100_converter/113_themes.asciidoc +1 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/documentation.adoc +2 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/multi-document/_includes/documents/100_chapter.asciidoc +3 -1
- data/lib/starter_web/pages/public/asciidoc_skeletons/multi-document/_includes/documents/200_chapter.asciidoc +1 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/multi-document/multi.adoc +2 -0
- data/lib/starter_web/pages/public/asciidoc_skeletons/simple-document/simple.adoc +6 -0
- data/lib/starter_web/pages/public/blog/navigator/archive/allview.html +6 -13
- data/lib/starter_web/pages/public/blog/navigator/archive/categoryview.html +17 -11
- data/lib/starter_web/pages/public/blog/navigator/archive/dateview.html +7 -8
- data/lib/starter_web/pages/public/blog/navigator/archive/tagview.html +5 -7
- data/lib/starter_web/pages/public/blog/navigator/index.html +15 -14
- data/lib/starter_web/pages/public/features/general.adoc +7 -1
- data/lib/starter_web/pages/public/features/template.adoc +87 -128
- data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_all_books.adoc +10 -2
- data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_biography.adoc +8 -3
- data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_fantasy.adoc +11 -4
- data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_romance.adoc +11 -5
- data/lib/starter_web/pages/public/learn/bookshelf/jekyll_collections.adoc +24 -14
- data/lib/starter_web/pages/public/learn/bs_sass_variables/bs_sass_variables.adoc +4 -0
- data/lib/starter_web/pages/public/learn/core_web_vitals/core_web_vitals.adoc +8 -4
- data/lib/starter_web/pages/public/learn/roundtrip/_includes/documents/100_gistblock.asciidoc +2 -1
- data/lib/starter_web/pages/public/learn/roundtrip/_includes/documents/themes_bootstrap.asciidoc +27 -28
- data/lib/starter_web/pages/public/learn/roundtrip/asciidoc_extensions.adoc +169 -155
- data/lib/starter_web/pages/public/learn/roundtrip/bootstrap_themes.adoc +28 -19
- data/lib/starter_web/pages/public/learn/roundtrip/highlghter_rouge.adoc +9 -5
- data/lib/starter_web/pages/public/learn/roundtrip/icon_fonts.adoc +67 -77
- data/lib/starter_web/pages/public/learn/roundtrip/lunr_search.1.asciidoc +460 -0
- data/lib/starter_web/pages/public/learn/roundtrip/lunr_search.adoc +12 -7
- data/lib/starter_web/pages/public/learn/roundtrip/modal_extentions.adoc +26 -19
- data/lib/starter_web/pages/public/learn/roundtrip/present_images.adoc +470 -447
- data/lib/starter_web/pages/public/learn/roundtrip/present_videos.adoc +60 -59
- data/lib/starter_web/pages/public/learn/roundtrip/responsive_tables.adoc +51 -38
- data/lib/starter_web/pages/public/learn/roundtrip/typography.adoc +31 -25
- data/lib/starter_web/pages/public/learn/where_to_go.adoc +9 -4
- data/lib/starter_web/pages/public/legal/en/100_copyright.adoc +4 -0
- data/lib/starter_web/pages/public/legal/en/200_impress.adoc +2 -0
- data/lib/starter_web/pages/public/legal/en/300_privacy.adoc +22 -3
- data/lib/starter_web/pages/public/legal/en/400_comment_policy.adoc +5 -0
- data/lib/starter_web/pages/public/manuals/speak2me.adoc +412 -0
- data/lib/starter_web/pages/public/panels/intro_panel/panel.adoc +27 -19
- data/lib/starter_web/pages/public/plans/plans.adoc +3 -0
- data/lib/starter_web/pages/public/tools/cheatsheet/gem.adoc +2 -0
- data/lib/starter_web/pages/public/tools/cheatsheet/git.adoc +2 -0
- data/lib/starter_web/pages/public/tools/cheatsheet/j1.adoc +2 -0
- data/lib/starter_web/pages/public/tools/cheatsheet/yaml.adoc +6 -0
- data/lib/starter_web/pages/public/tools/previewer/preview_bootstrap_theme.adoc +17 -3
- data/lib/starter_web/utilsrv/_defaults/package.json +1 -1
- data/lib/starter_web/utilsrv/package.json +1 -1
- metadata +38 -7
- 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
|
+
// (& 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));
|