j1-template 2023.6.0 → 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/unodark/bootstrap.css +43 -0
- data/assets/themes/j1/core/css/themes/unodark/bootstrap.min.css +1 -1
- data/assets/themes/j1/core/css/themes/unolight/bootstrap.css +141 -1
- data/assets/themes/j1/core/css/themes/unolight/bootstrap.min.css +1 -1
- 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 +4 -4
- data/lib/starter_web/README.md +5 -5
- data/lib/starter_web/_config.yml +7 -1
- 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 +3 -0
- data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_biography.adoc +1 -1
- data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_fantasy.adoc +1 -0
- data/lib/starter_web/pages/public/learn/bookshelf/article_previewer/viewer_romance.adoc +1 -1
- data/lib/starter_web/pages/public/learn/bookshelf/jekyll_collections.adoc +11 -8
- 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 +164 -153
- data/lib/starter_web/pages/public/learn/roundtrip/bootstrap_themes.adoc +22 -16
- data/lib/starter_web/pages/public/learn/roundtrip/highlghter_rouge.adoc +3 -2
- data/lib/starter_web/pages/public/learn/roundtrip/icon_fonts.adoc +62 -74
- 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 +4 -0
- data/lib/starter_web/pages/public/learn/roundtrip/modal_extentions.adoc +21 -15
- data/lib/starter_web/pages/public/learn/roundtrip/present_images.adoc +470 -449
- data/lib/starter_web/pages/public/learn/roundtrip/present_videos.adoc +53 -54
- data/lib/starter_web/pages/public/learn/roundtrip/responsive_tables.adoc +46 -34
- data/lib/starter_web/pages/public/learn/roundtrip/typography.adoc +25 -22
- data/lib/starter_web/pages/public/learn/where_to_go.adoc +6 -2
- 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
@@ -2542,6 +2542,16 @@ module.exports = function navigator(options) {
|
|
2542
2542
|
});
|
2543
2543
|
} // END Translator
|
2544
2544
|
|
2545
|
+
// ---------------------------------------------------------------------
|
2546
|
+
// Speak2Me dialog
|
2547
|
+
//
|
2548
|
+
if ($('li.speak')) {
|
2549
|
+
logger.debug('register SHOW event for J1 Speak2Me');
|
2550
|
+
$('li.speak > a', this).on('click', function (e) {
|
2551
|
+
j1.adapter.speak2me.showDialog();
|
2552
|
+
});
|
2553
|
+
} // END NBI Notebooks
|
2554
|
+
|
2545
2555
|
// ---------------------------------------------------------------------
|
2546
2556
|
// NBI Notebooks dialog
|
2547
2557
|
//
|
@@ -2957,6 +2967,1653 @@ module.exports = function scrollSmooth(options) {
|
|
2957
2967
|
|
2958
2968
|
/***/ }),
|
2959
2969
|
|
2970
|
+
/***/ 633:
|
2971
|
+
/***/ ((module) => {
|
2972
|
+
|
2973
|
+
// -----------------------------------------------------------------------------
|
2974
|
+
// ESLint shimming
|
2975
|
+
// -----------------------------------------------------------------------------
|
2976
|
+
/* eslint indent: "off" */
|
2977
|
+
/* eslint no-undef: "off" */
|
2978
|
+
/* eslint no-unused-vars: "off" */
|
2979
|
+
// -----------------------------------------------------------------------------
|
2980
|
+
|
2981
|
+
module.exports = {
|
2982
|
+
// Where to render the table of contents.
|
2983
|
+
tocSelector: '.js-toc',
|
2984
|
+
// Where to grab the headings to build the table of contents.
|
2985
|
+
contentSelector: '.js-toc-content',
|
2986
|
+
// Which headings to grab inside of the contentSelector element.
|
2987
|
+
headingSelector: 'h2, h3, h4, h5, h6',
|
2988
|
+
// Headings that match the ignoreSelector will be skipped.
|
2989
|
+
ignoreSelector: '.notoc',
|
2990
|
+
// For headings inside relative or absolute positioned containers within content
|
2991
|
+
hasInnerContainers: false,
|
2992
|
+
// Main class to add to links.
|
2993
|
+
linkClass: 'toc-link',
|
2994
|
+
// Extra classes to add to links.
|
2995
|
+
extraLinkClasses: '',
|
2996
|
+
// Class to add to active links,
|
2997
|
+
// the link corresponding to the top most heading on the page.
|
2998
|
+
activeLinkClass: 'is-active-link',
|
2999
|
+
// Main class to add to lists.
|
3000
|
+
listClass: 'toc-list',
|
3001
|
+
// Extra classes to add to lists.
|
3002
|
+
extraListClasses: '',
|
3003
|
+
// Class that gets added when a list should be collapsed.
|
3004
|
+
isCollapsedClass: 'is-collapsed',
|
3005
|
+
// Class that gets added when a list should be able
|
3006
|
+
// to be collapsed but isn't necessarily collapsed.
|
3007
|
+
collapsibleClass: 'is-collapsible',
|
3008
|
+
// Class to add to list items.
|
3009
|
+
listItemClass: 'toc-list-item',
|
3010
|
+
// Class to add to active list items.
|
3011
|
+
activeListItemClass: 'is-active-li',
|
3012
|
+
// How many heading levels should not be collapsed.
|
3013
|
+
// For example, number 6 will show everything since
|
3014
|
+
// there are only 6 heading levels and number 0 will collapse them all.
|
3015
|
+
// The sections that are hidden will open
|
3016
|
+
// and close as you scroll to headings within them.
|
3017
|
+
collapseDepth: 3,
|
3018
|
+
// Smooth scrolling enabled.
|
3019
|
+
scrollSmooth: true,
|
3020
|
+
// Smooth scroll duration.
|
3021
|
+
scrollSmoothDuration: 300,
|
3022
|
+
// Smooth scroll offset.
|
3023
|
+
scrollSmoothOffset: 0,
|
3024
|
+
// Callback for scroll end.
|
3025
|
+
scrollEndCallback: function (e) {},
|
3026
|
+
// Headings offset between the headings and the top of the document (this is meant for minor adjustments).
|
3027
|
+
headingsOffset: 1,
|
3028
|
+
// Timeout between events firing to make sure it's
|
3029
|
+
// not too rapid (for performance reasons).
|
3030
|
+
throttleTimeout: 150,
|
3031
|
+
// Element to add the positionFixedClass to.
|
3032
|
+
positionFixedSelector: null,
|
3033
|
+
// Fixed position class to add to make sidebar fixed after scrolling
|
3034
|
+
// down past the fixedSidebarOffset.
|
3035
|
+
positionFixedClass: 'is-position-fixed',
|
3036
|
+
// fixedSidebarOffset can be any number but by default is set
|
3037
|
+
// to auto which sets the fixedSidebarOffset to the sidebar
|
3038
|
+
// element's offsetTop from the top of the document on init.
|
3039
|
+
fixedSidebarOffset: 'auto',
|
3040
|
+
// includeHtml can be set to true to include the HTML markup from the
|
3041
|
+
// heading node instead of just including the textContent.
|
3042
|
+
includeHtml: false,
|
3043
|
+
// onclick function to apply to all links in toc. will be called with
|
3044
|
+
// the event as the first parameter, and this can be used to stop,
|
3045
|
+
// propagation, prevent default or perform action
|
3046
|
+
onClick: function (e) {},
|
3047
|
+
// orderedList can be set to false to generate unordered lists (ul)
|
3048
|
+
// instead of ordered lists (ol)
|
3049
|
+
orderedList: true,
|
3050
|
+
// If there is a fixed article scroll container, set to calculate titles' offset
|
3051
|
+
scrollContainer: null,
|
3052
|
+
// prevent ToC DOM rendering if it's already rendered by an external system
|
3053
|
+
skipRendering: false,
|
3054
|
+
// Optional callback to change heading labels.
|
3055
|
+
// For example it can be used to cut down and put ellipses on multiline headings you deem too long.
|
3056
|
+
// Called each time a heading is parsed. Expects a string in return, the modified label to display.
|
3057
|
+
// function (string) => string
|
3058
|
+
headingLabelCallback: false,
|
3059
|
+
// ignore headings that are hidden in DOM
|
3060
|
+
ignoreHiddenElements: false,
|
3061
|
+
// Optional callback to modify properties of parsed headings.
|
3062
|
+
// The heading element is passed in node parameter and information parsed by default parser is provided in obj parameter.
|
3063
|
+
// Function has to return the same or modified obj.
|
3064
|
+
// The heading will be excluded from TOC if nothing is returned.
|
3065
|
+
// function (object, HTMLElement) => object | void
|
3066
|
+
headingObjectCallback: null,
|
3067
|
+
// Set the base path, useful if you use a `base` tag in `head`.
|
3068
|
+
basePath: '',
|
3069
|
+
// Only takes affect when `tocSelector` is scrolling,
|
3070
|
+
// keep the toc scroll position in sync with the content.
|
3071
|
+
disableTocScrollSync: false
|
3072
|
+
};
|
3073
|
+
|
3074
|
+
/***/ }),
|
3075
|
+
|
3076
|
+
/***/ 291:
|
3077
|
+
/***/ ((module) => {
|
3078
|
+
|
3079
|
+
/**
|
3080
|
+
* This file is responsible for parsing the content from the DOM and making
|
3081
|
+
* sure data is nested properly.
|
3082
|
+
*
|
3083
|
+
* @author Tim Scanlin
|
3084
|
+
*/
|
3085
|
+
|
3086
|
+
// -----------------------------------------------------------------------------
|
3087
|
+
// ESLint shimming
|
3088
|
+
// -----------------------------------------------------------------------------
|
3089
|
+
/* eslint indent: "off" */
|
3090
|
+
/* eslint no-undef: "off" */
|
3091
|
+
/* eslint semi: "off" */
|
3092
|
+
// -----------------------------------------------------------------------------
|
3093
|
+
|
3094
|
+
module.exports = function parseContent(options) {
|
3095
|
+
var reduce = [].reduce;
|
3096
|
+
|
3097
|
+
/**
|
3098
|
+
* Get the last item in an array and return a reference to it.
|
3099
|
+
* @param {Array} array
|
3100
|
+
* @return {Object}
|
3101
|
+
*/
|
3102
|
+
function getLastItem(array) {
|
3103
|
+
return array[array.length - 1];
|
3104
|
+
}
|
3105
|
+
|
3106
|
+
/**
|
3107
|
+
* Get heading level for a heading dom node.
|
3108
|
+
* @param {HTMLElement} heading
|
3109
|
+
* @return {Number}
|
3110
|
+
*/
|
3111
|
+
function getHeadingLevel(heading) {
|
3112
|
+
return +heading.nodeName.split('H').join('');
|
3113
|
+
}
|
3114
|
+
|
3115
|
+
/**
|
3116
|
+
* Get important properties from a heading element and store in a plain object.
|
3117
|
+
* @param {HTMLElement} heading
|
3118
|
+
* @return {Object}
|
3119
|
+
*/
|
3120
|
+
function getHeadingObject(heading) {
|
3121
|
+
// each node is processed twice by this method because nestHeadingsArray() and addNode() calls it
|
3122
|
+
// first time heading is real DOM node element, second time it is obj
|
3123
|
+
// that is causing problem so I am processing only original DOM node
|
3124
|
+
if (!(heading instanceof window.HTMLElement)) return heading;
|
3125
|
+
if (options.ignoreHiddenElements && (!heading.offsetHeight || !heading.offsetParent)) {
|
3126
|
+
return null;
|
3127
|
+
}
|
3128
|
+
var obj = {
|
3129
|
+
id: heading.id,
|
3130
|
+
children: [],
|
3131
|
+
nodeName: heading.nodeName,
|
3132
|
+
headingLevel: getHeadingLevel(heading),
|
3133
|
+
textContent: options.headingLabelCallback ? String(options.headingLabelCallback(heading.textContent)) : heading.textContent.trim()
|
3134
|
+
};
|
3135
|
+
if (options.includeHtml) {
|
3136
|
+
obj.childNodes = heading.childNodes;
|
3137
|
+
}
|
3138
|
+
if (options.headingObjectCallback) {
|
3139
|
+
return options.headingObjectCallback(obj, heading);
|
3140
|
+
}
|
3141
|
+
return obj;
|
3142
|
+
}
|
3143
|
+
|
3144
|
+
/**
|
3145
|
+
* Add a node to the nested array.
|
3146
|
+
* @param {Object} node
|
3147
|
+
* @param {Array} nest
|
3148
|
+
* @return {Array}
|
3149
|
+
*/
|
3150
|
+
function addNode(node, nest) {
|
3151
|
+
var obj = getHeadingObject(node);
|
3152
|
+
var level = obj.headingLevel;
|
3153
|
+
var array = nest;
|
3154
|
+
var lastItem = getLastItem(array);
|
3155
|
+
var lastItemLevel = lastItem ? lastItem.headingLevel : 0;
|
3156
|
+
var counter = level - lastItemLevel;
|
3157
|
+
while (counter > 0) {
|
3158
|
+
lastItem = getLastItem(array);
|
3159
|
+
if (lastItem && lastItem.children !== undefined) {
|
3160
|
+
array = lastItem.children;
|
3161
|
+
}
|
3162
|
+
counter--;
|
3163
|
+
}
|
3164
|
+
if (level >= options.collapseDepth) {
|
3165
|
+
obj.isCollapsed = true;
|
3166
|
+
}
|
3167
|
+
array.push(obj);
|
3168
|
+
return array;
|
3169
|
+
}
|
3170
|
+
|
3171
|
+
/**
|
3172
|
+
* Select headings in content area, exclude any selector in options.ignoreSelector
|
3173
|
+
* @param {String} contentSelector
|
3174
|
+
* @param {Array} headingSelector
|
3175
|
+
* @return {Array}
|
3176
|
+
*/
|
3177
|
+
function selectHeadings(contentSelector, headingSelector) {
|
3178
|
+
var selectors = headingSelector;
|
3179
|
+
if (options.ignoreSelector) {
|
3180
|
+
selectors = headingSelector.split(',').map(function mapSelectors(selector) {
|
3181
|
+
return selector.trim() + ':not(' + options.ignoreSelector + ')';
|
3182
|
+
});
|
3183
|
+
}
|
3184
|
+
try {
|
3185
|
+
return document.querySelector(contentSelector).querySelectorAll(selectors);
|
3186
|
+
} catch (e) {
|
3187
|
+
console.warn('Element not found: ' + contentSelector); // eslint-disable-line
|
3188
|
+
return null;
|
3189
|
+
}
|
3190
|
+
}
|
3191
|
+
|
3192
|
+
/**
|
3193
|
+
* Nest headings array into nested arrays with 'children' property.
|
3194
|
+
* @param {Array} headingsArray
|
3195
|
+
* @return {Object}
|
3196
|
+
*/
|
3197
|
+
function nestHeadingsArray(headingsArray) {
|
3198
|
+
return reduce.call(headingsArray, function reducer(prev, curr) {
|
3199
|
+
var currentHeading = getHeadingObject(curr);
|
3200
|
+
if (currentHeading) {
|
3201
|
+
addNode(currentHeading, prev.nest);
|
3202
|
+
}
|
3203
|
+
return prev;
|
3204
|
+
}, {
|
3205
|
+
nest: []
|
3206
|
+
});
|
3207
|
+
}
|
3208
|
+
return {
|
3209
|
+
nestHeadingsArray: nestHeadingsArray,
|
3210
|
+
selectHeadings: selectHeadings
|
3211
|
+
};
|
3212
|
+
};
|
3213
|
+
|
3214
|
+
/***/ }),
|
3215
|
+
|
3216
|
+
/***/ 229:
|
3217
|
+
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
|
3218
|
+
|
3219
|
+
/*
|
3220
|
+
# -----------------------------------------------------------------------------
|
3221
|
+
# ~/assets/themes/j1/modules/speak2me/js/speak2me.js
|
3222
|
+
# speak2me v.1.0 implementation (based on Articulate.js) for J1 Theme
|
3223
|
+
#
|
3224
|
+
# Product/Info:
|
3225
|
+
# https://jekyll.one
|
3226
|
+
# https://github.com/acoti/articulate.js/tree/master
|
3227
|
+
#
|
3228
|
+
# Copyright (C) 2023 Juergen Adams
|
3229
|
+
# Copyright (C) 2017 Adam Coti
|
3230
|
+
#
|
3231
|
+
# J1 Theme is licensed under the MIT License.
|
3232
|
+
# See: https://github.com/jekyll-one-org/j1-template/blob/main/LICENSE.md
|
3233
|
+
# Articulate is licensed under the MIT License.
|
3234
|
+
# See: https://github.com/acoti/articulate.js/blob/master/LICENSE
|
3235
|
+
# -----------------------------------------------------------------------------
|
3236
|
+
*/
|
3237
|
+
|
3238
|
+
/* Articulate.js (1.1.0). (C) 2017 Adam Coti.
|
3239
|
+
MIT @license: en.wikipedia.org/wiki/MIT_License
|
3240
|
+
See Github page at: https://github.com/acoti/articulate.js
|
3241
|
+
See Web site at: http://articulate.purefreedom.com
|
3242
|
+
*/
|
3243
|
+
|
3244
|
+
(function ($) {
|
3245
|
+
'use strict';
|
3246
|
+
|
3247
|
+
const defaultOptions = __webpack_require__(633);
|
3248
|
+
const ParseContent = __webpack_require__(291);
|
3249
|
+
const parseContent = ParseContent(defaultOptions);
|
3250
|
+
const scrollBehavior = 'smooth';
|
3251
|
+
const speechCycle = 10;
|
3252
|
+
const speechMonitorCycle = 10;
|
3253
|
+
const textSliceLength = 30;
|
3254
|
+
const minWords = 3;
|
3255
|
+
const pageScanCycle = 1000;
|
3256
|
+
const pageScanLines = 10000;
|
3257
|
+
const isFirefox = /Firefox/i.test(navigator.userAgent);
|
3258
|
+
const isEdge = /Edg/i.test(navigator.userAgent);
|
3259
|
+
const chrome = /chrome/i.test(navigator.userAgent);
|
3260
|
+
const isChrome = chrome && !isEdge;
|
3261
|
+
const voiceUserDefault = 'Google UK English Female';
|
3262
|
+
const voiceChromeDefault = 'Google US English';
|
3263
|
+
const ignoreProvider = 'Microsoft';
|
3264
|
+
const defaultLanguage = 'en-GB';
|
3265
|
+
var currentTranslation = getCookie('googtrans');
|
3266
|
+
var scrollBlockOffset = 100;
|
3267
|
+
var customOptions = {};
|
3268
|
+
var myOptions = {};
|
3269
|
+
var ignoreTagsUser = new Array();
|
3270
|
+
var recognizeTagsUser = new Array();
|
3271
|
+
var replacements = new Array();
|
3272
|
+
var customTags = new Array();
|
3273
|
+
var voices = new Array();
|
3274
|
+
var headingsArray = [];
|
3275
|
+
var rateDefault = 0.9;
|
3276
|
+
var pitchDefault = 1;
|
3277
|
+
var volumeDefault = 0.9;
|
3278
|
+
var rate = rateDefault;
|
3279
|
+
var pitch = pitchDefault;
|
3280
|
+
var volume = volumeDefault;
|
3281
|
+
var pause_spoken = '. ';
|
3282
|
+
var chunkCounter = 0;
|
3283
|
+
var userStoppedSpeaking = false;
|
3284
|
+
var chunkSpoken = false;
|
3285
|
+
var scrollOnce = true;
|
3286
|
+
var rateUserDefault;
|
3287
|
+
var pitchUserDefault;
|
3288
|
+
var volumeUserDefault;
|
3289
|
+
var currentLanguage;
|
3290
|
+
var voiceLanguageDefault;
|
3291
|
+
var chunkCounterMax;
|
3292
|
+
var user_session;
|
3293
|
+
var scanFinished;
|
3294
|
+
var voiceLanguageGoogleDefault = {
|
3295
|
+
'de-DE': 'Google Deutsch',
|
3296
|
+
'en-US': 'Google US English',
|
3297
|
+
'en-GB': 'Google UK English Female',
|
3298
|
+
'es-ES': 'Google español',
|
3299
|
+
'fr-FR': 'Google français',
|
3300
|
+
// 'hi-IN': 'Google हिन्दी',
|
3301
|
+
// 'id-ID': 'Google Bahasa Indonesia',
|
3302
|
+
'it-IT': 'Google italiano',
|
3303
|
+
// 'jp-JP': 'Google 日本語',
|
3304
|
+
// 'ko-KR': 'Google 한국의',
|
3305
|
+
'nl-NL': 'Google Nederlands',
|
3306
|
+
'pl-PL': 'Google polski',
|
3307
|
+
// 'pt-BR': 'Google português do Brasil',
|
3308
|
+
'pt-PT': 'Google português do Brasil'
|
3309
|
+
// 'ru-RU': 'Google русский',
|
3310
|
+
// 'zh-CN': 'Google 普通话(中国大陆)',
|
3311
|
+
};
|
3312
|
+
|
3313
|
+
var voiceLanguageMicrosoftDefault = {
|
3314
|
+
'sq-AL': 'Microsoft Anila Online (Natural) - Albanian (Albania)',
|
3315
|
+
'ar-EG': 'Microsoft Salma Online (Natural) - Arabic (Egypt)',
|
3316
|
+
'bg-BG': 'Microsoft Kalina Online (Natural) - Bulgarian (Bulgaria)',
|
3317
|
+
'zh-CN': 'Microsoft Xiaoxiao Online (Natural) - Chinese (Mainland)',
|
3318
|
+
'hr-HR': 'Microsoft Gabrijela Online (Natural) - Croatian (Croatia)',
|
3319
|
+
'cs-CZ': 'Microsoft Antonin Online (Natural) - Czech (Czech)',
|
3320
|
+
'da-DK': 'Microsoft Christel Online (Natural) - Danish (Denmark)',
|
3321
|
+
'nl-NL': 'Microsoft Colette Online (Natural) - Dutch (Netherlands)',
|
3322
|
+
'en-GB': 'Microsoft Libby Online (Natural) - English (United Kingdom)',
|
3323
|
+
'en-US': 'Microsoft Aria Online (Natural) - English (United States)',
|
3324
|
+
'et-EE': 'Microsoft Anu Online (Natural) - Estonian (Estonia)',
|
3325
|
+
'fi-FI': 'Microsoft Noora Online (Natural) - Finnish (Finland)',
|
3326
|
+
'fr-FR': 'Microsoft Denise Online (Natural) - French (France)',
|
3327
|
+
'ka-GE': 'Microsoft Giorgi Online (Natural) - Georgian (Georgia)',
|
3328
|
+
'de-DE': 'Microsoft Katja Online (Natural) - German (Germany)',
|
3329
|
+
'el-GR': 'Microsoft Athina Online (Natural) - Greek (Greece)',
|
3330
|
+
'he-IL': 'Microsoft Avri Online (Natural) - Hebrew (Israel)',
|
3331
|
+
'hi-IN': 'Microsoft Madhur Online (Natural) - Hindi (India)',
|
3332
|
+
'hu-HU': 'Microsoft Noemi Online (Natural) - Hungarian (Hungary)',
|
3333
|
+
'it-IT': 'Microsoft Elsa Online (Natural) - Italian (Italy)',
|
3334
|
+
'ja-JP': 'Microsoft Nanami Online (Natural) - Japanese (Japan)'
|
3335
|
+
};
|
3336
|
+
var voiceLanguageFirefoxDefault = {
|
3337
|
+
'en-GB': 'Microsoft Hazel - English (United Kingdom) (en-GB)',
|
3338
|
+
'en-US': 'Microsoft Zira Desktop - English (United States) (en-US)',
|
3339
|
+
'de-DE': 'Microsoft Katja Online (Natural) - German (Germany)'
|
3340
|
+
};
|
3341
|
+
|
3342
|
+
// -------------------------------------------------------------------------
|
3343
|
+
// Internal functions
|
3344
|
+
// -------------------------------------------------------------------------
|
3345
|
+
|
3346
|
+
// scan a page to get correct positions for scrolling and highlightning
|
3347
|
+
//
|
3348
|
+
function scanPage(options) {
|
3349
|
+
// see: https://stackoverflow.com/questions/3163615/how-to-scroll-an-html-page-to-a-given-anchor
|
3350
|
+
// see: https://stackoverflow.com/questions/22154129/how-to-make-setinterval-behave-more-in-sync-or-how-to-use-settimeout-instea
|
3351
|
+
var line = 0;
|
3352
|
+
var lines;
|
3353
|
+
function scanSection(counter) {
|
3354
|
+
// because of the current translation in progress, the length
|
3355
|
+
// of a page may change to higher or lower values (asian)
|
3356
|
+
//
|
3357
|
+
lines = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
|
3358
|
+
$('#content').attr("style", "opacity: .3");
|
3359
|
+
if (line < lines) {
|
3360
|
+
setTimeout(function () {
|
3361
|
+
counter++;
|
3362
|
+
line = line + pageScanLines;
|
3363
|
+
window.scrollTo({
|
3364
|
+
top: line,
|
3365
|
+
behavior: 'smooth'
|
3366
|
+
});
|
3367
|
+
scanSection(counter);
|
3368
|
+
}, pageScanCycle);
|
3369
|
+
} else {
|
3370
|
+
setTimeout(function () {
|
3371
|
+
scanFinished = true;
|
3372
|
+
$('#content').attr("style", "opacity: 1");
|
3373
|
+
$(window).scrollTop(0);
|
3374
|
+
}, pageScanCycle);
|
3375
|
+
}
|
3376
|
+
}
|
3377
|
+
scanSection(0);
|
3378
|
+
} // END scanPage
|
3379
|
+
|
3380
|
+
// merge (configuration) objects
|
3381
|
+
//
|
3382
|
+
function extend() {
|
3383
|
+
var target = {};
|
3384
|
+
for (var i = 0; i < arguments.length; i++) {
|
3385
|
+
var source = arguments[i];
|
3386
|
+
for (var key in source) {
|
3387
|
+
if (hasOwnProperty.call(source, key)) {
|
3388
|
+
target[key] = source[key];
|
3389
|
+
}
|
3390
|
+
}
|
3391
|
+
}
|
3392
|
+
return target;
|
3393
|
+
} // END extend
|
3394
|
+
|
3395
|
+
// get the conten of a Cookie (by its name)
|
3396
|
+
//
|
3397
|
+
function getCookie(name) {
|
3398
|
+
var nameEQ = name + '=';
|
3399
|
+
var ca = document.cookie.split(';');
|
3400
|
+
for (var i = 0; i < ca.length; i++) {
|
3401
|
+
var c = ca[i];
|
3402
|
+
while (c.charAt(0) === ' ') {
|
3403
|
+
c = c.substring(1, c.length);
|
3404
|
+
}
|
3405
|
+
if (c.indexOf(nameEQ) === 0) {
|
3406
|
+
var value = c.substring(nameEQ.length, c.length);
|
3407
|
+
return value;
|
3408
|
+
}
|
3409
|
+
}
|
3410
|
+
return undefined;
|
3411
|
+
} // END getCookie
|
3412
|
+
|
3413
|
+
function voiceTag(prepend, append) {
|
3414
|
+
this.prepend = prepend;
|
3415
|
+
this.append = append;
|
3416
|
+
} // END voiceTag
|
3417
|
+
|
3418
|
+
function voiceObj(name, language) {
|
3419
|
+
this.name = name;
|
3420
|
+
this.language = language;
|
3421
|
+
} // END voiceObj
|
3422
|
+
|
3423
|
+
// count the number of words in a string
|
3424
|
+
//
|
3425
|
+
function wordCount(str) {
|
3426
|
+
var count = 0;
|
3427
|
+
var words = str.split(" ");
|
3428
|
+
for (var i = 0; i < words.length; i++) {
|
3429
|
+
// inner loop -- do the count
|
3430
|
+
if (words[i] != "") {
|
3431
|
+
count += 1;
|
3432
|
+
}
|
3433
|
+
}
|
3434
|
+
return count;
|
3435
|
+
} // END wordCount
|
3436
|
+
|
3437
|
+
// This populates the "voices" array with objects that represent the
|
3438
|
+
// available voices in the current browser. Each object has two
|
3439
|
+
// properties: name and language. It is loaded asynchronously in
|
3440
|
+
// deference to Chrome.
|
3441
|
+
//
|
3442
|
+
function populateVoiceList() {
|
3443
|
+
var systemVoices = speechSynthesis.getVoices();
|
3444
|
+
for (var i = 0; i < systemVoices.length; i++) {
|
3445
|
+
voices.push(new voiceObj(systemVoices[i].name, systemVoices[i].lang));
|
3446
|
+
}
|
3447
|
+
} // END populateVoiceList
|
3448
|
+
|
3449
|
+
populateVoiceList();
|
3450
|
+
if (typeof speechSynthesis !== 'undefined' && speechSynthesis.onvoiceschanged !== undefined) {
|
3451
|
+
speechSynthesis.onvoiceschanged = populateVoiceList;
|
3452
|
+
}
|
3453
|
+
|
3454
|
+
// After checking for compatability, define the utterance object and
|
3455
|
+
// then cancel the speech immediately even though nothing is yet spoken.
|
3456
|
+
// This is to fix a quirk in Windows Chrome.
|
3457
|
+
//
|
3458
|
+
if ('speechSynthesis' in window) {
|
3459
|
+
var speech = new SpeechSynthesisUtterance();
|
3460
|
+
window.speechSynthesis.cancel();
|
3461
|
+
}
|
3462
|
+
if (currentTranslation === undefined) {
|
3463
|
+
// currentLanguage = 'en-US'
|
3464
|
+
currentLanguage = defaultLanguage;
|
3465
|
+
} else {
|
3466
|
+
var translation = currentTranslation.split('/');
|
3467
|
+
if (translation[2] == 'en') {
|
3468
|
+
currentLanguage = 'en-GB';
|
3469
|
+
} else if (translation[2].includes('ar')) {
|
3470
|
+
currentLanguage = 'ar-EG';
|
3471
|
+
} else if (translation[2].includes('cs')) {
|
3472
|
+
currentLanguage = 'cs-CZ';
|
3473
|
+
} else if (translation[2].includes('da')) {
|
3474
|
+
currentLanguage = 'da-DK';
|
3475
|
+
} else if (translation[2].includes('en')) {
|
3476
|
+
currentLanguage = 'en-UK';
|
3477
|
+
} else if (translation[2].includes('et')) {
|
3478
|
+
currentLanguage = 'et-EE';
|
3479
|
+
} else if (translation[2].includes('ka')) {
|
3480
|
+
currentLanguage = 'ka-GE';
|
3481
|
+
} else if (translation[2].includes('el')) {
|
3482
|
+
currentLanguage = 'el-GR';
|
3483
|
+
} else if (translation[2].includes('iw')) {
|
3484
|
+
currentLanguage = 'he-IL';
|
3485
|
+
} else if (translation[2].includes('hi')) {
|
3486
|
+
currentLanguage = 'hi-IN';
|
3487
|
+
} else if (translation[2].includes('ja')) {
|
3488
|
+
currentLanguage = 'ja-JP';
|
3489
|
+
} else if (translation[2].includes('zh')) {
|
3490
|
+
currentLanguage = 'zh-CN';
|
3491
|
+
} else {
|
3492
|
+
currentLanguage = translation[2] + '-' + translation[2].toUpperCase();
|
3493
|
+
}
|
3494
|
+
}
|
3495
|
+
if (isChrome) {
|
3496
|
+
var voiceLanguageDefault = voiceLanguageGoogleDefault[currentLanguage];
|
3497
|
+
}
|
3498
|
+
if (isEdge) {
|
3499
|
+
var voiceLanguageDefault = voiceLanguageMicrosoftDefault[currentLanguage];
|
3500
|
+
}
|
3501
|
+
if (isFirefox) {
|
3502
|
+
var voiceLanguageDefault = voiceLanguageFirefoxDefault[currentLanguage];
|
3503
|
+
}
|
3504
|
+
|
3505
|
+
// -------------------------------------------------------------------------
|
3506
|
+
// Public functions (methods)
|
3507
|
+
// -------------------------------------------------------------------------
|
3508
|
+
//
|
3509
|
+
var methods = {
|
3510
|
+
// main speak2me method.
|
3511
|
+
//
|
3512
|
+
speak: function (options) {
|
3513
|
+
var opts = $.extend({}, $.fn.speak2me.defaults, options);
|
3514
|
+
var toSpeak = '';
|
3515
|
+
var voiceTags = new Array();
|
3516
|
+
var _this = this;
|
3517
|
+
var obj, processed, finished;
|
3518
|
+
var ignoreTags;
|
3519
|
+
scanFinished = false;
|
3520
|
+
myOptions = extend(defaultOptions, customOptions || {});
|
3521
|
+
|
3522
|
+
// scan page to find correct positions for scrolling and highlightning
|
3523
|
+
//
|
3524
|
+
scanPage();
|
3525
|
+
|
3526
|
+
// Default values
|
3527
|
+
//
|
3528
|
+
voiceTags['a'] = new voiceTag('Link, ', pause_spoken);
|
3529
|
+
voiceTags['q'] = new voiceTag('', pause_spoken);
|
3530
|
+
voiceTags['ol'] = new voiceTag('Start of list.', 'End of list. ');
|
3531
|
+
voiceTags['ul'] = new voiceTag('Start of list.', 'End of list. ');
|
3532
|
+
voiceTags['dl'] = new voiceTag('Start of list.', 'End of list. ');
|
3533
|
+
voiceTags['dt'] = new voiceTag('', ', ');
|
3534
|
+
voiceTags['img'] = new voiceTag('Start of an embedded image with the description,', ', ');
|
3535
|
+
voiceTags['table'] = new voiceTag('Start of an embedded table,', 'This element ist not spoken.');
|
3536
|
+
voiceTags['card-header'] = new voiceTag('', '');
|
3537
|
+
voiceTags['doc-example'] = new voiceTag('Start of an embedded example element,', 'This element ist not spoken.');
|
3538
|
+
voiceTags['admonitionblock'] = new voiceTag('Start of an attention element of type, ', '');
|
3539
|
+
voiceTags['listingblock'] = new voiceTag('Start of an embedded structured text block,', 'This element ist not spoken.');
|
3540
|
+
voiceTags['carousel'] = new voiceTag('Start of an embedded carousel element,', 'This element ist not spoken.');
|
3541
|
+
voiceTags['slider'] = new voiceTag('Start of an embedded slider element,', 'This element ist not spoken.');
|
3542
|
+
voiceTags['masonry'] = new voiceTag('Start of an embedded masonry element,', 'This element ist not spoken.');
|
3543
|
+
voiceTags['lightbox'] = new voiceTag('Start of an embedded lightbox element,', 'This element ist not spoken.');
|
3544
|
+
voiceTags['gallery'] = new voiceTag('Start of an embedded gallery element,', 'This element ist not spoken.');
|
3545
|
+
voiceTags['figure'] = new voiceTag('Start of an embedded figure with the caption,', '');
|
3546
|
+
voiceTags['blockquote'] = new voiceTag('Blockquote start.', 'Blockquote end.');
|
3547
|
+
voiceTags['quoteblock'] = new voiceTag('Start of an embedded quote block element,', 'Quote block element end.');
|
3548
|
+
|
3549
|
+
// ignoreTags = ['masonry', 'carousel', 'slider', 'pre','audio','button','canvas','code','del','dialog','embed','form','head','iframe','meter','nav','noscript','object','s','script','select','style','textarea','video'];
|
3550
|
+
ignoreTags = ['audio', 'button', 'canvas', 'code', 'del', 'pre', 'dialog', 'embed', 'form', 'head', 'iframe', 'meter', 'nav', 'noscript', 'object', 's', 'script', 'select', 'style', 'textarea', 'video'];
|
3551
|
+
|
3552
|
+
// TODO: NOT working for multiple 'tab' windows
|
3553
|
+
// dispayed in the same browser
|
3554
|
+
//
|
3555
|
+
// If something is currently being spoken, ignore new voice
|
3556
|
+
// request. Otherwise it would be queued, which is doable if
|
3557
|
+
// someone wanted that, but not what I wanted.
|
3558
|
+
//
|
3559
|
+
if (window.speechSynthesis.speaking) {
|
3560
|
+
return;
|
3561
|
+
}
|
3562
|
+
;
|
3563
|
+
|
3564
|
+
// TODO: coincident active speech synthesis in multiple
|
3565
|
+
// browser windows or tabs does NOT work
|
3566
|
+
//
|
3567
|
+
// user_session = j1.readCookie('j1.user.session');
|
3568
|
+
// if (user_session.speech_synthesis_active === true) {
|
3569
|
+
// return
|
3570
|
+
// };
|
3571
|
+
|
3572
|
+
// user_session.speech_synthesis_active = true;
|
3573
|
+
// j1.writeCookie({
|
3574
|
+
// name: 'user_session',
|
3575
|
+
// data: user_session,
|
3576
|
+
// secure: false,
|
3577
|
+
// expires: 0
|
3578
|
+
// });
|
3579
|
+
|
3580
|
+
// top-level function to prepare the HTML content of a page
|
3581
|
+
// and transform the resulting (speakable) text into chunks
|
3582
|
+
//
|
3583
|
+
var processSpeech = setInterval(function () {
|
3584
|
+
if (scanFinished) {
|
3585
|
+
// Cycle through all the elements in the original jQuery
|
3586
|
+
// selector, process and clean them one at a time, and
|
3587
|
+
// continually append it to the variable "toSpeak".
|
3588
|
+
//
|
3589
|
+
_this.each(function () {
|
3590
|
+
obj = $(this).clone(); // clone the DOM node and its descendants of what the user wants spoken
|
3591
|
+
processed = processDOMelements(obj); // process and manipulate DOM tree of this clone
|
3592
|
+
processed = jQuery(processed).html(); // convert the result of all that to a string
|
3593
|
+
finished = cleanDOMelements(processed); // do some text manipulation
|
3594
|
+
toSpeak = finished;
|
3595
|
+
});
|
3596
|
+
|
3597
|
+
// Check if users have set their own rate/pitch/volume
|
3598
|
+
// defaults, otherwise use defaults
|
3599
|
+
//
|
3600
|
+
if (rateUserDefault !== undefined) {
|
3601
|
+
rate = rateUserDefault;
|
3602
|
+
} else {
|
3603
|
+
rate = rateDefault;
|
3604
|
+
}
|
3605
|
+
if (pitchUserDefault !== undefined) {
|
3606
|
+
pitch = pitchUserDefault;
|
3607
|
+
} else {
|
3608
|
+
pitch = pitchDefault;
|
3609
|
+
}
|
3610
|
+
if (volumeUserDefault !== undefined) {
|
3611
|
+
volume = volumeUserDefault;
|
3612
|
+
} else {
|
3613
|
+
volume = volumeDefault;
|
3614
|
+
}
|
3615
|
+
|
3616
|
+
// create and configure the utterance object
|
3617
|
+
//
|
3618
|
+
speech = new SpeechSynthesisUtterance();
|
3619
|
+
speech.rate = rate;
|
3620
|
+
speech.pitch = pitch;
|
3621
|
+
speech.volume = volume;
|
3622
|
+
speech.voice = speechSynthesis.getVoices().filter(function (voice) {
|
3623
|
+
return voice.name == voiceLanguageDefault;
|
3624
|
+
})[0];
|
3625
|
+
speech.previousScrollPosition = 0;
|
3626
|
+
processTextChunks(speech, toSpeak);
|
3627
|
+
clearInterval(processSpeech);
|
3628
|
+
}
|
3629
|
+
}, speechCycle); // END processSpeech
|
3630
|
+
|
3631
|
+
// create the chunks array from (speakable) text generated
|
3632
|
+
//
|
3633
|
+
function splitTextIntoChunks(text) {
|
3634
|
+
var chunks = [];
|
3635
|
+
|
3636
|
+
// strip strange elements from text
|
3637
|
+
// unclear why a elements of ' >' is generated in text
|
3638
|
+
// may caused by a HTML tag
|
3639
|
+
text = text.replace(/^\s+>/gm, '');
|
3640
|
+
|
3641
|
+
// cleanup text
|
3642
|
+
text = text.replace(/(\r\n|\n|\r)/gm, '');
|
3643
|
+
text = text.replace(/\s+/gm, ' ');
|
3644
|
+
chunks = text.split('.');
|
3645
|
+
|
3646
|
+
// 1st cleanup of chunks
|
3647
|
+
chunks.forEach((chunk, index) => {
|
3648
|
+
chunks[index] = chunks[index].replace(/^\s+/g, '');
|
3649
|
+
chunks[index] = chunks[index].replaceAll('""', '');
|
3650
|
+
});
|
3651
|
+
|
3652
|
+
// 2nd cleanup of chunks (delete chunks NOT speakable)
|
3653
|
+
chunks.forEach((chunk, index) => {
|
3654
|
+
if (chunks[index].length > 0) {
|
3655
|
+
chunks[index] = chunks[index] + '. ';
|
3656
|
+
} else {
|
3657
|
+
// remove empty text element from chunks array
|
3658
|
+
chunks.splice(index, 1);
|
3659
|
+
}
|
3660
|
+
});
|
3661
|
+
|
3662
|
+
// 3rd cleanup of chunks (delete empty chunks)
|
3663
|
+
chunks.forEach((chunk, index) => {
|
3664
|
+
if (chunks[index].length == 0) {
|
3665
|
+
// remove empty text element from chunks array
|
3666
|
+
chunks.splice(index, 1);
|
3667
|
+
}
|
3668
|
+
});
|
3669
|
+
|
3670
|
+
// build the chunk OBJECT array
|
3671
|
+
//
|
3672
|
+
var chunkSet = [];
|
3673
|
+
chunks.forEach((chunk, index) => {
|
3674
|
+
var text = chunks[index];
|
3675
|
+
var sectionText = textSlice(text, textSliceLength, minWords);
|
3676
|
+
var $paragraph = $('#content').find("p:contains('" + sectionText + "')");
|
3677
|
+
var offset;
|
3678
|
+
if ($paragraph.length > 0) {
|
3679
|
+
offset = Math.round($paragraph[0].offsetTop);
|
3680
|
+
} else {
|
3681
|
+
offset = undefined;
|
3682
|
+
$paragraph = undefined;
|
3683
|
+
}
|
3684
|
+
chunkSet.push({
|
3685
|
+
text: text,
|
3686
|
+
offsetTop: offset,
|
3687
|
+
$paragraph: $paragraph
|
3688
|
+
});
|
3689
|
+
});
|
3690
|
+
|
3691
|
+
// Get headings array
|
3692
|
+
headingsArray = parseContent.selectHeadings(defaultOptions.contentSelector, defaultOptions.headingSelector);
|
3693
|
+
|
3694
|
+
// parse the headingsArray to add missing offset values
|
3695
|
+
// for headlines
|
3696
|
+
//
|
3697
|
+
chunkSet.forEach((chunk, index) => {
|
3698
|
+
var text;
|
3699
|
+
var innerText;
|
3700
|
+
if (chunk.offset === undefined) {
|
3701
|
+
// cleanup the spoken text for compare
|
3702
|
+
text = chunk.text.replaceAll('. ', '');
|
3703
|
+
|
3704
|
+
// jadams:
|
3705
|
+
// for this type of loop, NOT all headings are found in the array
|
3706
|
+
if (headingsArray !== null) {
|
3707
|
+
// see: https://stackoverflow.com/questions/29285897/difference-between-for-in-and-for-of-statements
|
3708
|
+
// for in loops over enumerable property names of an object
|
3709
|
+
// for of (new in ES6) does use an object-specific iterator
|
3710
|
+
// and loops over the values generated by that.
|
3711
|
+
for (var node of headingsArray) {
|
3712
|
+
// for (var node in headingsArray) {
|
3713
|
+
// cleanup the innerText for compare
|
3714
|
+
innerText = node.innerText.replaceAll('?', '');
|
3715
|
+
innerText = node.innerText;
|
3716
|
+
if (innerText == text) {
|
3717
|
+
var headline = $('#' + node.id);
|
3718
|
+
if (headline.length > 0) {
|
3719
|
+
var offsetTop = headline.offset().top;
|
3720
|
+
chunk.offsetTop = Math.round(offsetTop);
|
3721
|
+
// console.debug('speak2me, text: ' + node.innerText + ', offsetTop: ' + chunk.offsetTop);
|
3722
|
+
} else {
|
3723
|
+
// console.warn('speak2me, text: ' + node.innerText + ', offsetTop not caclulated.');
|
3724
|
+
}
|
3725
|
+
}
|
3726
|
+
}
|
3727
|
+
}
|
3728
|
+
}
|
3729
|
+
});
|
3730
|
+
return chunkSet;
|
3731
|
+
}
|
3732
|
+
|
3733
|
+
// create a slice of text used later to identify the
|
3734
|
+
// containing paragraph
|
3735
|
+
//
|
3736
|
+
function textSlice(text, slicelenght, wordsMin) {
|
3737
|
+
var startSubString = 0;
|
3738
|
+
var endSubString = startSubString + slicelenght;
|
3739
|
+
var subText = text.substr(startSubString, endSubString);
|
3740
|
+
var stringArray = subText.split(/(\s+)/);
|
3741
|
+
var words;
|
3742
|
+
|
3743
|
+
// Remove last two elements are a fraction of subText
|
3744
|
+
stringArray.pop();
|
3745
|
+
stringArray.pop();
|
3746
|
+
|
3747
|
+
// built the new string
|
3748
|
+
subText = stringArray.join('');
|
3749
|
+
subText = subText.replaceAll('.', '');
|
3750
|
+
|
3751
|
+
// at least two words required
|
3752
|
+
words = wordCount(subText);
|
3753
|
+
if (words < wordsMin) {
|
3754
|
+
return undefined;
|
3755
|
+
console.warn('no search possible on this fraction of subText');
|
3756
|
+
} else {
|
3757
|
+
return subText;
|
3758
|
+
}
|
3759
|
+
}
|
3760
|
+
|
3761
|
+
// process chunks (to speak) sequentially
|
3762
|
+
//
|
3763
|
+
function processTextChunks(speaker, chunks) {
|
3764
|
+
const synth = window.speechSynthesis;
|
3765
|
+
|
3766
|
+
// indicate active converter in the quicklinks bar
|
3767
|
+
//
|
3768
|
+
$('.mdib-speaker').addClass('mdib-spin');
|
3769
|
+
|
3770
|
+
// manage scrolling and highlightning for the active spoken text
|
3771
|
+
//
|
3772
|
+
speaker.addEventListener('start', event => {
|
3773
|
+
// store current scroll position
|
3774
|
+
//
|
3775
|
+
if (speaker.offsetTop !== undefined) {
|
3776
|
+
speaker.currentScrollPosition = speaker.offsetTop;
|
3777
|
+
}
|
3778
|
+
|
3779
|
+
// adjust scrolling position offsetTop for 'post series'
|
3780
|
+
//
|
3781
|
+
if ($('.bmd-layout-header').length) {
|
3782
|
+
speaker.offsetTop = $('.bmd-layout-header')[0].offsetTop + (scrollBlockOffset / 2 + 3) + speaker.offsetTop;
|
3783
|
+
speaker.previousScrollPosition = speaker.offsetTop;
|
3784
|
+
}
|
3785
|
+
|
3786
|
+
// add highlightning
|
3787
|
+
//
|
3788
|
+
if (speaker.$paragraph !== undefined) {
|
3789
|
+
speaker.$paragraph.addClass('speak-highlighted');
|
3790
|
+
}
|
3791
|
+
|
3792
|
+
// scroll to paragraph currently spoken if a valid
|
3793
|
+
// offsetTop is available
|
3794
|
+
//
|
3795
|
+
if (speaker.offsetTop !== undefined) {
|
3796
|
+
if (speaker.offsetTop >= speaker.previousScrollPosition) {
|
3797
|
+
window.scrollTo({
|
3798
|
+
top: speaker.offsetTop - scrollBlockOffset,
|
3799
|
+
behavior: scrollBehavior
|
3800
|
+
});
|
3801
|
+
}
|
3802
|
+
}
|
3803
|
+
});
|
3804
|
+
|
3805
|
+
// listener to manage highlightning for spoken text elements
|
3806
|
+
// and set next chunk to speak
|
3807
|
+
//
|
3808
|
+
speaker.addEventListener('end', function (event) {
|
3809
|
+
// workaround WRONG offsetTop positions (LOWER as expected)
|
3810
|
+
//
|
3811
|
+
if (speaker.offsetTop !== undefined) {
|
3812
|
+
if (speaker.offsetTop >= speaker.previousScrollPosition) {
|
3813
|
+
speaker.previousScrollPosition = speaker.offsetTop;
|
3814
|
+
}
|
3815
|
+
}
|
3816
|
+
|
3817
|
+
// remove highlightning for the paragraph already spoken
|
3818
|
+
//
|
3819
|
+
if (speaker.$paragraph !== undefined) {
|
3820
|
+
speaker.$paragraph.removeClass('speak-highlighted');
|
3821
|
+
}
|
3822
|
+
chunkSpoken = false;
|
3823
|
+
chunkCounter++;
|
3824
|
+
});
|
3825
|
+
|
3826
|
+
// loop to prepare chunks to speak or sto the voice output
|
3827
|
+
//
|
3828
|
+
var speechMonitor = setInterval(function () {
|
3829
|
+
// check if all chunks (text) are spoken
|
3830
|
+
//
|
3831
|
+
if (chunkCounter == chunkCounterMax || userStoppedSpeaking) {
|
3832
|
+
chunkCounter = 0;
|
3833
|
+
userStoppedSpeaking = false;
|
3834
|
+
chunkSpoken = false;
|
3835
|
+
speaker.$paragraph !== undefined && speaker.$paragraph.removeClass('speak-highlighted');
|
3836
|
+
window.scrollTo({
|
3837
|
+
top: 0,
|
3838
|
+
behavior: 'smooth'
|
3839
|
+
});
|
3840
|
+
$('.mdib-speaker').removeClass('mdib-spin');
|
3841
|
+
clearInterval(speechMonitor);
|
3842
|
+
} else {
|
3843
|
+
// prepare speaker data and start the voice
|
3844
|
+
//
|
3845
|
+
speaker.text = chunks[chunkCounter].text;
|
3846
|
+
speaker.offsetTop = chunks[chunkCounter].offsetTop;
|
3847
|
+
speaker.$paragraph = chunks[chunkCounter].$paragraph;
|
3848
|
+
|
3849
|
+
// speak current text line
|
3850
|
+
if (!chunkSpoken) {
|
3851
|
+
synth.speak(speaker);
|
3852
|
+
chunkSpoken = true;
|
3853
|
+
}
|
3854
|
+
}
|
3855
|
+
}, speechMonitorCycle); // END speechMonitor
|
3856
|
+
} // END processTextChunks
|
3857
|
+
|
3858
|
+
// transform all configured DOM elements of a page that
|
3859
|
+
// can be spoken into plain text
|
3860
|
+
//
|
3861
|
+
function processDOMelements(clone) {
|
3862
|
+
var copy, title, title_element, content_type, content_element, content, appended, prepend;
|
3863
|
+
|
3864
|
+
// Remove tags from the "ignoreTags" array because the
|
3865
|
+
// user called "speak2me('recognize')" and said he/she
|
3866
|
+
// doesn't want some tags un-spoken. Double negative there,
|
3867
|
+
// but it does make sense.
|
3868
|
+
//
|
3869
|
+
if (recognizeTagsUser.length > 0) {
|
3870
|
+
for (var prop in recognizeTagsUser) {
|
3871
|
+
var index = ignoreTags.indexOf(recognizeTagsUser[prop]);
|
3872
|
+
if (index > -1) {
|
3873
|
+
ignoreTags.splice(index, 1);
|
3874
|
+
}
|
3875
|
+
}
|
3876
|
+
}
|
3877
|
+
;
|
3878
|
+
|
3879
|
+
// Remove DOM objects listed in the "ignoreTags" array
|
3880
|
+
//
|
3881
|
+
for (var prop in ignoreTags) {
|
3882
|
+
jQuery(clone).find(ignoreTags[prop]).addBack(ignoreTags[prop]).not('[data-speak2me-recognize]').each(function () {
|
3883
|
+
jQuery(this).html('');
|
3884
|
+
});
|
3885
|
+
}
|
3886
|
+
;
|
3887
|
+
|
3888
|
+
// Remove DOM objects specified in the "ignoreTagsUser" array
|
3889
|
+
// Specified when calling "speak2me('ignore')".
|
3890
|
+
//
|
3891
|
+
if (ignoreTagsUser.length > 0) {
|
3892
|
+
for (var prop in ignoreTagsUser) {
|
3893
|
+
jQuery(clone).find(ignoreTagsUser[prop]).addBack(ignoreTagsUser[prop]).not('[data-speak2me-recognize]').each(function () {
|
3894
|
+
jQuery(this).html('');
|
3895
|
+
});
|
3896
|
+
}
|
3897
|
+
;
|
3898
|
+
}
|
3899
|
+
;
|
3900
|
+
|
3901
|
+
// Remove DOM objects marked in the HTML code by "data-speak2me-ignore".
|
3902
|
+
//
|
3903
|
+
jQuery(clone).find('[data-speak2me-ignore]').addBack('[data-speak2me-ignore]').each(function () {
|
3904
|
+
jQuery(this).html('');
|
3905
|
+
});
|
3906
|
+
|
3907
|
+
// Remove DOM objects in the HTML code mareked by class "speak2me-ignore".
|
3908
|
+
//
|
3909
|
+
jQuery(clone).find('.speak2me-ignore').addBack('[data-speak2me-ignore]').each(function () {
|
3910
|
+
jQuery(this).html('');
|
3911
|
+
});
|
3912
|
+
|
3913
|
+
// Search for prepend data specified in the HTML code by "data-speak2me-prepend".
|
3914
|
+
//
|
3915
|
+
jQuery(clone).find('[data-speak2me-prepend]').addBack('[data-speak2me-prepend]').each(function () {
|
3916
|
+
copy = jQuery(this).data('speak2me-prepend');
|
3917
|
+
jQuery(this).prepend(copy + ' ');
|
3918
|
+
});
|
3919
|
+
|
3920
|
+
// Search for append data specified in the HTML code by "data-speak2me-append".
|
3921
|
+
//
|
3922
|
+
jQuery(clone).find('[data-speak2me-append]').addBack('[data-speak2me-append]').each(function () {
|
3923
|
+
copy = jQuery(this).data('speak2me-append');
|
3924
|
+
jQuery(this).append(' ' + copy);
|
3925
|
+
});
|
3926
|
+
|
3927
|
+
// Search for tags to prepend and append specified by the "voiceTags" array.
|
3928
|
+
//
|
3929
|
+
var count = 0;
|
3930
|
+
for (var tag in voiceTags) {
|
3931
|
+
jQuery(clone).find(tag).each(function () {
|
3932
|
+
if (customTags[tag]) {
|
3933
|
+
jQuery(this).prepend(customTags[tag].prepend + ' ');
|
3934
|
+
jQuery(this).append(' ' + customTags[tag].append);
|
3935
|
+
} else {
|
3936
|
+
jQuery(this).prepend(voiceTags[tag].prepend + ' ');
|
3937
|
+
jQuery(this).append(' ' + voiceTags[tag].append);
|
3938
|
+
}
|
3939
|
+
;
|
3940
|
+
});
|
3941
|
+
}
|
3942
|
+
;
|
3943
|
+
|
3944
|
+
// Search for <h1> through <h6> and <li> and <br> to add
|
3945
|
+
// a pause at the end of those tags. This is done
|
3946
|
+
// because these tags require a pause, but often don't
|
3947
|
+
// have a comma or period at the end of their text.
|
3948
|
+
//
|
3949
|
+
jQuery(clone).find('h1,h2,h3,h4,h5,h6,li,p').addBack('h1,h2,h3,h4,h5,h6,li,p').each(function () {
|
3950
|
+
jQuery(this).append(pause_spoken);
|
3951
|
+
});
|
3952
|
+
|
3953
|
+
// Search for <br> tags to add a pause at the end.
|
3954
|
+
jQuery(clone).find('br').each(function () {
|
3955
|
+
jQuery(this).after(pause_spoken);
|
3956
|
+
});
|
3957
|
+
|
3958
|
+
// Search for <figure>, check for <figcaption>, insert
|
3959
|
+
// that text if exists and then remove the whole DOM object.
|
3960
|
+
//
|
3961
|
+
jQuery(clone).find('figure').addBack('figure').each(function () {
|
3962
|
+
copy = jQuery(this).find('figcaption').html();
|
3963
|
+
if (customTags['figure']) {
|
3964
|
+
prepend = customTags['figure'].prepend;
|
3965
|
+
} else {
|
3966
|
+
prepend = voiceTags['figure'].prepend;
|
3967
|
+
}
|
3968
|
+
if (copy != undefined && copy !== '') {
|
3969
|
+
jQuery('<div>' + prepend + ' ' + copy + '</div>').insertBefore(this);
|
3970
|
+
}
|
3971
|
+
jQuery(this).remove();
|
3972
|
+
});
|
3973
|
+
|
3974
|
+
// Search for <image> tags, check for ALT attribute, and insert
|
3975
|
+
// text if exists and then dinally remove the DOM object.
|
3976
|
+
// NOTE: had to make adjustments for nesting in <picture> tags.
|
3977
|
+
//
|
3978
|
+
jQuery(clone).find('img').addBack('img').each(function () {
|
3979
|
+
copy = jQuery(this).attr('alt');
|
3980
|
+
var parent = jQuery(this).parent();
|
3981
|
+
var parentName = parent.get(0).tagName;
|
3982
|
+
if (customTags['img']) {
|
3983
|
+
prepend = customTags['img'].prepend;
|
3984
|
+
} else {
|
3985
|
+
prepend = voiceTags['img'].prepend;
|
3986
|
+
}
|
3987
|
+
if (copy !== undefined && copy != '') {
|
3988
|
+
if (parentName == 'PICTURE') {
|
3989
|
+
var par;
|
3990
|
+
jQuery('<div>' + prepend + ' ' + copy + pause_spoken + '</div>').insertBefore(parent);
|
3991
|
+
} else {
|
3992
|
+
jQuery('<div>' + prepend + ' ' + copy + pause_spoken + '</div>').insertBefore(this);
|
3993
|
+
}
|
3994
|
+
}
|
3995
|
+
jQuery(this).remove();
|
3996
|
+
});
|
3997
|
+
|
3998
|
+
// TODO: identify why the text 'Follow the link' is
|
3999
|
+
// already placed on the anchor
|
4000
|
+
//
|
4001
|
+
jQuery(clone).find('a').addBack('a').each(function () {
|
4002
|
+
var anchor = jQuery(this);
|
4003
|
+
copy = anchor[0].innerText;
|
4004
|
+
prepend = voiceTags['a'].prepend;
|
4005
|
+
appended = voiceTags['a'].append;
|
4006
|
+
|
4007
|
+
// jQuery('<div>' + prepend + copy + '</div>').insertBefore(this);
|
4008
|
+
jQuery('<div>' + copy + '</div>').insertBefore(this);
|
4009
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4010
|
+
jQuery(this).remove();
|
4011
|
+
});
|
4012
|
+
|
4013
|
+
// Search for admonitionblock elements and extract the type and
|
4014
|
+
// content. Insert type and content and then remove the DOM object.
|
4015
|
+
//
|
4016
|
+
jQuery(clone).find('.admonitionblock').addBack('admonitionblock').each(function () {
|
4017
|
+
content_type = this.classList[1];
|
4018
|
+
content_element = jQuery(this).find('.content');
|
4019
|
+
content = content_element[0].innerText;
|
4020
|
+
prepend = voiceTags['admonitionblock'].prepend + content_type + '. ';
|
4021
|
+
appended = voiceTags['admonitionblock'].append;
|
4022
|
+
if (content !== undefined && content != '') {
|
4023
|
+
jQuery('<div>' + prepend + ' ' + content + '</div>').insertBefore(this);
|
4024
|
+
jQuery('<div>' + appended + pause_spoken + '</div>').insertBefore(this);
|
4025
|
+
}
|
4026
|
+
jQuery(this).remove();
|
4027
|
+
});
|
4028
|
+
|
4029
|
+
// Search for quote block elements.
|
4030
|
+
//
|
4031
|
+
jQuery(clone).find('.quoteblock').addBack('quoteblock').each(function () {
|
4032
|
+
var attribution = jQuery(this).find('.attribution');
|
4033
|
+
content_element = jQuery(this).find('blockquote');
|
4034
|
+
content = content_element[0].innerText + 'quoted by, ' + attribution[0].innerText + ', ';
|
4035
|
+
prepend = voiceTags['quoteblock'].prepend;
|
4036
|
+
appended = voiceTags['quoteblock'].append;
|
4037
|
+
if (content !== undefined && content != '') {
|
4038
|
+
jQuery('<div>' + prepend + ' ' + content + '</div>').insertBefore(this);
|
4039
|
+
jQuery('<div>' + appended + pause_spoken + '</div>').insertBefore(this);
|
4040
|
+
}
|
4041
|
+
jQuery(this).remove();
|
4042
|
+
});
|
4043
|
+
|
4044
|
+
// Search for <table>, check for <caption>, insert text
|
4045
|
+
// if exists and then remove the DOM object.
|
4046
|
+
//
|
4047
|
+
jQuery(clone).find('table').addBack('table').each(function () {
|
4048
|
+
copy = jQuery(this).find('caption').text();
|
4049
|
+
prepend = voiceTags['table'].prepend;
|
4050
|
+
appended = voiceTags['table'].append;
|
4051
|
+
if (copy !== undefined && copy != '') {
|
4052
|
+
jQuery('<div>' + prepend + ' ' + copy + '</div>').insertBefore(this);
|
4053
|
+
jQuery('<div>' + appended + pause_spoken + '</div>').insertBefore(this);
|
4054
|
+
}
|
4055
|
+
jQuery(this).remove();
|
4056
|
+
});
|
4057
|
+
|
4058
|
+
// Search for cards|header elements and then remove the DOM object.
|
4059
|
+
//
|
4060
|
+
jQuery(clone).find('.card-header').addBack('card-header').each(function () {
|
4061
|
+
title_element = jQuery(this).find('.card-title');
|
4062
|
+
prepend = voiceTags['card-header'].prepend;
|
4063
|
+
appended = voiceTags['card-header'].append;
|
4064
|
+
if (title_element.length) {
|
4065
|
+
title = title_element[0].innerText + pause_spoken;
|
4066
|
+
} else {
|
4067
|
+
title = '';
|
4068
|
+
}
|
4069
|
+
jQuery('<div>' + prepend + '</div>').insertBefore(this);
|
4070
|
+
jQuery('<div>' + appended + title + '</div>').insertBefore(this);
|
4071
|
+
jQuery(title_element).remove();
|
4072
|
+
});
|
4073
|
+
|
4074
|
+
// Search for doc-example elements and then remove the DOM object.
|
4075
|
+
//
|
4076
|
+
jQuery(clone).find('.doc-example').addBack('doc-example').each(function () {
|
4077
|
+
prepend = voiceTags['doc-example'].prepend;
|
4078
|
+
appended = voiceTags['doc-example'].append;
|
4079
|
+
jQuery('<div>' + prepend + '</div>').insertBefore(this);
|
4080
|
+
jQuery('<div>' + appended + pause_spoken + '</div>').insertBefore(this);
|
4081
|
+
jQuery(this).remove();
|
4082
|
+
});
|
4083
|
+
|
4084
|
+
// Search for listingblock elements, check for previous declared <div>
|
4085
|
+
// container that contains the title element and insert the
|
4086
|
+
// text if exists and then finally remove the DOM object.
|
4087
|
+
//
|
4088
|
+
jQuery(clone).find('.listingblock').addBack('listingblock').each(function () {
|
4089
|
+
title_element = jQuery(this).find('.title');
|
4090
|
+
if (title_element.length) {
|
4091
|
+
copy = title_element[0].innerText;
|
4092
|
+
} else {
|
4093
|
+
copy = '';
|
4094
|
+
}
|
4095
|
+
prepend = voiceTags['listingblock'].prepend;
|
4096
|
+
appended = voiceTags['listingblock'].append;
|
4097
|
+
if (copy !== undefined && copy != '') {
|
4098
|
+
jQuery('<div>' + prepend + ' with the caption,' + copy + pause_spoken + '</div>').insertBefore(this);
|
4099
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4100
|
+
} else {
|
4101
|
+
jQuery('<div>' + prepend + '</div>').insertBefore(this);
|
4102
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4103
|
+
}
|
4104
|
+
jQuery(this).remove();
|
4105
|
+
});
|
4106
|
+
|
4107
|
+
// Search for <carousel> tags, check for previous declared <div>
|
4108
|
+
// container that contains the title element and insert the
|
4109
|
+
// text if exists and finally remove the DOM object.
|
4110
|
+
//
|
4111
|
+
jQuery(clone).find('carousel').addBack('carousel').each(function () {
|
4112
|
+
if ($(this).prev()[0].innerText !== undefined) {
|
4113
|
+
title = $(this).prev()[0].innerText;
|
4114
|
+
title_element = jQuery(this).prev();
|
4115
|
+
// remove the title 'before' the DOM object deleted
|
4116
|
+
//
|
4117
|
+
jQuery(title_element).remove();
|
4118
|
+
} else {
|
4119
|
+
title = '';
|
4120
|
+
}
|
4121
|
+
prepend = voiceTags['carousel'].prepend;
|
4122
|
+
appended = voiceTags['carousel'].append;
|
4123
|
+
if (title !== undefined && title != '') {
|
4124
|
+
jQuery('<div>' + prepend + ' with the caption,' + title + pause_spoken + '</div>').insertBefore(this);
|
4125
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4126
|
+
} else {
|
4127
|
+
jQuery('<div>' + prepend + '</div>').insertBefore(this);
|
4128
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4129
|
+
}
|
4130
|
+
jQuery(this).remove();
|
4131
|
+
});
|
4132
|
+
|
4133
|
+
// Search for <slider> tags, check for previous declared <div>
|
4134
|
+
// container that contains the title element and insert the
|
4135
|
+
// text if exists and finally remove the DOM object.
|
4136
|
+
//
|
4137
|
+
jQuery(clone).find('slider').addBack('slider').each(function () {
|
4138
|
+
if ($(this).prev()[0].innerText !== undefined) {
|
4139
|
+
title = $(this).prev()[0].innerText;
|
4140
|
+
title_element = jQuery(this).prev();
|
4141
|
+
// remove the title 'before' the DOM object deleted
|
4142
|
+
//
|
4143
|
+
jQuery(title_element).remove();
|
4144
|
+
} else {
|
4145
|
+
title = '';
|
4146
|
+
}
|
4147
|
+
prepend = voiceTags['slider'].prepend;
|
4148
|
+
appended = voiceTags['slider'].append;
|
4149
|
+
if (title !== undefined && title != '') {
|
4150
|
+
jQuery('<div>' + prepend + ' with the caption, ' + title + pause_spoken + '</div>').insertBefore(this);
|
4151
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4152
|
+
} else {
|
4153
|
+
jQuery('<div>' + prepend + '</div>').insertBefore(this);
|
4154
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4155
|
+
}
|
4156
|
+
jQuery(this).remove();
|
4157
|
+
});
|
4158
|
+
|
4159
|
+
// Search for <gallery> tags, check for previous declared <div>
|
4160
|
+
// container that contains the title element and insert the
|
4161
|
+
// text if exists and finally remove the DOM object.
|
4162
|
+
//
|
4163
|
+
jQuery(clone).find('gallery').addBack('gallery').each(function () {
|
4164
|
+
if ($(this).prev()[0].innerText !== undefined) {
|
4165
|
+
title = $(this).prev()[0].innerText;
|
4166
|
+
title_element = jQuery(this).prev();
|
4167
|
+
// remove the title BEFORE the DOM object gets deleted
|
4168
|
+
//
|
4169
|
+
jQuery(title_element).remove();
|
4170
|
+
} else {
|
4171
|
+
title = '';
|
4172
|
+
}
|
4173
|
+
prepend = voiceTags['gallery'].prepend;
|
4174
|
+
appended = voiceTags['gallery'].append;
|
4175
|
+
if (title !== undefined && title != '') {
|
4176
|
+
prepend !== '' && jQuery('<div>' + prepend + ' with the caption ' + title + pause_spoken + '</div>').insertBefore(this);
|
4177
|
+
appended !== '' && jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4178
|
+
} else {
|
4179
|
+
prepend !== '' && jQuery('<div>' + prepend + '</div>').insertBefore(this);
|
4180
|
+
appended !== '' && jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4181
|
+
}
|
4182
|
+
jQuery(this).remove();
|
4183
|
+
});
|
4184
|
+
|
4185
|
+
// Search for <slider> tags, and extract the <caption> tag data,
|
4186
|
+
// insert the text if exists and finally remove the DOM object.
|
4187
|
+
//
|
4188
|
+
jQuery(clone).find('lightbox').addBack('gallery').each(function () {
|
4189
|
+
if ($(this).prev()[0].innerText !== undefined) {
|
4190
|
+
title = $(this).prev()[0].innerText;
|
4191
|
+
title_element = jQuery(this).prev();
|
4192
|
+
// remove the title 'before' the DOM object deleted
|
4193
|
+
//
|
4194
|
+
jQuery(title_element).remove();
|
4195
|
+
} else {
|
4196
|
+
title = '';
|
4197
|
+
}
|
4198
|
+
prepend = voiceTags['lightbox'].prepend;
|
4199
|
+
appended = voiceTags['lightbox'].append;
|
4200
|
+
if (title !== undefined && title != '') {
|
4201
|
+
jQuery('<div>' + prepend + ' with the caption,' + title + pause_spoken + '</div>').insertBefore(this);
|
4202
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4203
|
+
} else {
|
4204
|
+
jQuery('<div>' + prepend + '</div>').insertBefore(this);
|
4205
|
+
jQuery('<div>' + appended + '</div>').insertBefore(this);
|
4206
|
+
}
|
4207
|
+
jQuery(this).remove();
|
4208
|
+
});
|
4209
|
+
|
4210
|
+
// Search for DOM object to be replaced specified in
|
4211
|
+
// the HTML code by "data-speak2me-swap".
|
4212
|
+
//
|
4213
|
+
jQuery(clone).find('[data-speak2me-swap]').addBack('[data-speak2me-swap]').each(function () {
|
4214
|
+
copy = jQuery(this).data('speak2me-swap');
|
4215
|
+
jQuery(this).text(copy);
|
4216
|
+
});
|
4217
|
+
|
4218
|
+
// Search for DOM object to spelled out specified in
|
4219
|
+
// the HTML code by "data-speak2me-spell".
|
4220
|
+
//
|
4221
|
+
jQuery(clone).find('[data-speak2me-spell]').addBack('[data-speak2me-spell]').each(function () {
|
4222
|
+
copy = jQuery(this).text();
|
4223
|
+
copy = copy.split('').join(' ');
|
4224
|
+
jQuery(this).text(copy);
|
4225
|
+
});
|
4226
|
+
return clone;
|
4227
|
+
}
|
4228
|
+
|
4229
|
+
// run final cleanups on all DOM elements processed.
|
4230
|
+
//
|
4231
|
+
function cleanDOMelements(final) {
|
4232
|
+
var start, ended, speak, part1, part2, final;
|
4233
|
+
|
4234
|
+
// Search for <speak2me> in comments, copy the text,
|
4235
|
+
// place it outside the comment, and then splice together
|
4236
|
+
// "final" string again, which omits the comment.
|
4237
|
+
//
|
4238
|
+
while (final.indexOf('<!-- <speak2me>') != -1) {
|
4239
|
+
start = final.indexOf('<!-- <speak2me>');
|
4240
|
+
ended = final.indexOf('</speak2me> -->', start);
|
4241
|
+
if (ended == -1) {
|
4242
|
+
break;
|
4243
|
+
}
|
4244
|
+
speak = final.substring(start + 17, ended);
|
4245
|
+
part1 = final.substring(0, start);
|
4246
|
+
part2 = final.substring(ended + 17);
|
4247
|
+
final = part1 + ' ' + speak + ' ' + part2;
|
4248
|
+
}
|
4249
|
+
;
|
4250
|
+
|
4251
|
+
// Strip out remaining comments.
|
4252
|
+
//
|
4253
|
+
final = final.replace(/<!--[\s\S]*?-->/g, '');
|
4254
|
+
|
4255
|
+
// Strip out remaining HTML tags.
|
4256
|
+
//
|
4257
|
+
final = final.replace(/(<([^>]+)>)/ig, '');
|
4258
|
+
|
4259
|
+
// Replace a string of characters with another as specified
|
4260
|
+
// by "speak2me('replace')".
|
4261
|
+
//
|
4262
|
+
var len = replacements.length;
|
4263
|
+
var i = 0;
|
4264
|
+
var old, rep;
|
4265
|
+
while (i < len) {
|
4266
|
+
old = replacements[i];
|
4267
|
+
old = old.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
4268
|
+
rep = replacements[i + 1] + ' ';
|
4269
|
+
var regexp = new RegExp(old, 'gi');
|
4270
|
+
var final = final.replace(regexp, rep);
|
4271
|
+
i = i + 2;
|
4272
|
+
}
|
4273
|
+
|
4274
|
+
// Remove double quotes.
|
4275
|
+
//
|
4276
|
+
final = final.replaceAll('"', '');
|
4277
|
+
final = final.replaceAll('“', '');
|
4278
|
+
final = final.replaceAll('”', '');
|
4279
|
+
|
4280
|
+
// Remove all colon ':' and replace by a dot.
|
4281
|
+
//
|
4282
|
+
final = final.replaceAll(':', '.');
|
4283
|
+
|
4284
|
+
// Replace all strange '., ' by a pause.
|
4285
|
+
//
|
4286
|
+
final = final.replaceAll('., ', '. ');
|
4287
|
+
final = final.replaceAll(' , ', ', ');
|
4288
|
+
|
4289
|
+
// Remove strange double pause elements.
|
4290
|
+
//
|
4291
|
+
final = final.replaceAll('. .', '');
|
4292
|
+
final = final.replaceAll(', .', '');
|
4293
|
+
final = final.replaceAll(' , ', '');
|
4294
|
+
|
4295
|
+
// Replace empty lines.
|
4296
|
+
//
|
4297
|
+
final = final.replace(/^$/g, '\n');
|
4298
|
+
final = final.replace(/^\s+$/g, '\n');
|
4299
|
+
|
4300
|
+
// Replace single full stops in line ' . ' or '. '.
|
4301
|
+
//
|
4302
|
+
final = final.replace(/\s+\.\s+/g, '\n');
|
4303
|
+
final = final.replace(/\s+\.\s+$/g, '\n');
|
4304
|
+
|
4305
|
+
// Replace strange double full stops '..'.
|
4306
|
+
//
|
4307
|
+
final = final.replace(/\.\./g, '.');
|
4308
|
+
|
4309
|
+
// Replace the abbreviations '.e.g.', 'E.g.' and 'etc.'.
|
4310
|
+
//
|
4311
|
+
final = final.replaceAll('e.g.', 'for example');
|
4312
|
+
final = final.replaceAll('E.g.', 'For example, ');
|
4313
|
+
final = final.replaceAll('etc.', 'and so on, ');
|
4314
|
+
|
4315
|
+
// Replace language specific abbreviations.
|
4316
|
+
// NOTE: may required for some voices|languages (like Gewrman)
|
4317
|
+
//
|
4318
|
+
final = final.replaceAll('z. B.', 'zum Beispiel, ');
|
4319
|
+
|
4320
|
+
// Remove question and exclamation (?|!) marks.
|
4321
|
+
//
|
4322
|
+
final = final.replace(/[\!\?]/g, '. ');
|
4323
|
+
|
4324
|
+
// Replace em-dashes and double-dashes with a pause
|
4325
|
+
// since most voices doesn't do so when speaking.
|
4326
|
+
//
|
4327
|
+
final = final.replaceAll('—', pause_spoken);
|
4328
|
+
final = final.replaceAll('–', pause_spoken);
|
4329
|
+
final = final.replaceAll('--', pause_spoken);
|
4330
|
+
|
4331
|
+
// When read from the DOM, a few special characters
|
4332
|
+
// (& for example) display as their hex codes
|
4333
|
+
// rather than resolving into their actual character.
|
4334
|
+
//
|
4335
|
+
var txt = document.createElement('textarea');
|
4336
|
+
txt.innerHTML = final;
|
4337
|
+
final = txt.value;
|
4338
|
+
|
4339
|
+
// split the final text in to chunks (sentences).
|
4340
|
+
//
|
4341
|
+
const textChunks = splitTextIntoChunks(final);
|
4342
|
+
chunkCounterMax = textChunks.length;
|
4343
|
+
return textChunks;
|
4344
|
+
}
|
4345
|
+
|
4346
|
+
// return the Utterance object of the SpeechSynthesis API.
|
4347
|
+
//
|
4348
|
+
return speech;
|
4349
|
+
},
|
4350
|
+
// END speak
|
4351
|
+
|
4352
|
+
pause: function () {
|
4353
|
+
window.speechSynthesis.pause();
|
4354
|
+
return this;
|
4355
|
+
},
|
4356
|
+
// END pause
|
4357
|
+
|
4358
|
+
resume: function () {
|
4359
|
+
window.speechSynthesis.resume();
|
4360
|
+
return this;
|
4361
|
+
},
|
4362
|
+
// END resume
|
4363
|
+
|
4364
|
+
// jadams
|
4365
|
+
stop: function () {
|
4366
|
+
window.speechSynthesis.cancel();
|
4367
|
+
userStoppedSpeaking = true;
|
4368
|
+
|
4369
|
+
// jadams
|
4370
|
+
// NOTE: stopping coincident active speech synthesis
|
4371
|
+
// in multiple browser windows (tabs) does NOT work
|
4372
|
+
//
|
4373
|
+
// user_session.speech_synthesis_active = false;
|
4374
|
+
// j1.writeCookie({
|
4375
|
+
// name: 'user_session',
|
4376
|
+
// data: user_session,
|
4377
|
+
// secure: false,
|
4378
|
+
// expires: 0
|
4379
|
+
// });
|
4380
|
+
|
4381
|
+
return this;
|
4382
|
+
},
|
4383
|
+
// END stop
|
4384
|
+
|
4385
|
+
enabled: function () {
|
4386
|
+
return 'speechSynthesis' in window;
|
4387
|
+
},
|
4388
|
+
// END enabled
|
4389
|
+
|
4390
|
+
isSpeaking: function () {
|
4391
|
+
return window.speechSynthesis.speaking;
|
4392
|
+
},
|
4393
|
+
// END is Speaking
|
4394
|
+
|
4395
|
+
isPaused: function () {
|
4396
|
+
return window.speechSynthesis.paused;
|
4397
|
+
},
|
4398
|
+
// END isPaused
|
4399
|
+
|
4400
|
+
rate: function () {
|
4401
|
+
var num = arguments[0];
|
4402
|
+
if (num >= 0.1 && num <= 10) {
|
4403
|
+
rateUserDefault = num;
|
4404
|
+
} else if (num === undefined) {
|
4405
|
+
rateUserDefault = void 0;
|
4406
|
+
rate = rateDefault;
|
4407
|
+
}
|
4408
|
+
return this;
|
4409
|
+
},
|
4410
|
+
// END rate
|
4411
|
+
|
4412
|
+
pitch: function () {
|
4413
|
+
var num = arguments[0];
|
4414
|
+
if (num >= 0.1 && num <= 2) {
|
4415
|
+
pitchUserDefault = num;
|
4416
|
+
} else if (num === undefined) {
|
4417
|
+
pitchUserDefault = void 0;
|
4418
|
+
pitch = pitchDefault;
|
4419
|
+
}
|
4420
|
+
return this;
|
4421
|
+
},
|
4422
|
+
// END pitch
|
4423
|
+
|
4424
|
+
volume: function () {
|
4425
|
+
var num = arguments[0];
|
4426
|
+
if (num >= 0 && num <= 1) {
|
4427
|
+
volumeUserDefault = num;
|
4428
|
+
} else if (num === undefined) {
|
4429
|
+
volumeUserDefault = void 0;
|
4430
|
+
volume = volumeDefault;
|
4431
|
+
}
|
4432
|
+
;
|
4433
|
+
return this;
|
4434
|
+
},
|
4435
|
+
// END volume
|
4436
|
+
|
4437
|
+
ignore: function () {
|
4438
|
+
var len = arguments.length;
|
4439
|
+
ignoreTagsUser.length = 0;
|
4440
|
+
while (len > 0) {
|
4441
|
+
len--;
|
4442
|
+
ignoreTagsUser.push(arguments[len]);
|
4443
|
+
}
|
4444
|
+
;
|
4445
|
+
return this;
|
4446
|
+
},
|
4447
|
+
// END ignore
|
4448
|
+
|
4449
|
+
recognize: function () {
|
4450
|
+
var len = arguments.length;
|
4451
|
+
recognizeTagsUser.length = 0;
|
4452
|
+
while (len > 0) {
|
4453
|
+
len--;
|
4454
|
+
recognizeTagsUser.push(arguments[len]);
|
4455
|
+
}
|
4456
|
+
return this;
|
4457
|
+
},
|
4458
|
+
// END recognize
|
4459
|
+
|
4460
|
+
replace: function () {
|
4461
|
+
var len = arguments.length;
|
4462
|
+
replacements.length = 0;
|
4463
|
+
var i = 0;
|
4464
|
+
while (i < len) {
|
4465
|
+
replacements.push(arguments[i], arguments[i + 1]);
|
4466
|
+
i = i + 2;
|
4467
|
+
if (len - i == 1) {
|
4468
|
+
break;
|
4469
|
+
}
|
4470
|
+
;
|
4471
|
+
}
|
4472
|
+
;
|
4473
|
+
return this;
|
4474
|
+
},
|
4475
|
+
// END replace
|
4476
|
+
|
4477
|
+
customize: function () {
|
4478
|
+
var len = arguments.length;
|
4479
|
+
if (len == 0) {
|
4480
|
+
customTags = [];
|
4481
|
+
}
|
4482
|
+
if (len == 2) {
|
4483
|
+
if (['img', 'table', 'figure'].indexOf(arguments[0]) == -1) {
|
4484
|
+
console.warn("When customizing, tag indicated must be either 'img', 'table', or 'figure'.");
|
4485
|
+
return;
|
4486
|
+
}
|
4487
|
+
customTags[arguments[0].toString()] = new voiceTag(arguments[1].toString());
|
4488
|
+
}
|
4489
|
+
if (len == 3) {
|
4490
|
+
if (['q', "ol", "ul", "blockquote"].indexOf(arguments[0]) == -1) {
|
4491
|
+
console.warn("When customizing, tag indicated must be either 'q', 'ol', 'ul' or 'blockquote'.");
|
4492
|
+
return;
|
4493
|
+
}
|
4494
|
+
customTags[arguments[0].toString()] = new voiceTag(arguments[1].toString(), arguments[2].toString());
|
4495
|
+
}
|
4496
|
+
return this;
|
4497
|
+
},
|
4498
|
+
// END customize
|
4499
|
+
|
4500
|
+
getVoices: function () {
|
4501
|
+
// If no arguments, then the user has requested the array of
|
4502
|
+
// voices populated earlier.
|
4503
|
+
//
|
4504
|
+
if (arguments.length == 0) {
|
4505
|
+
return voices;
|
4506
|
+
}
|
4507
|
+
|
4508
|
+
// If there's another argument, we'll assume it's a jQuery
|
4509
|
+
// selector designating where to put the dropdown menu.
|
4510
|
+
// And if there's a third argument, that will be custom text
|
4511
|
+
// for the dropdown menu.
|
4512
|
+
// Then we'll create a dropdown menu with the voice names and,
|
4513
|
+
//in parenthesis, the language code.
|
4514
|
+
//
|
4515
|
+
var obj = jQuery(arguments[0]);
|
4516
|
+
var selectionTxt = 'Choose a voice';
|
4517
|
+
if (arguments[1] !== undefined) {
|
4518
|
+
selectionTxt = arguments[1];
|
4519
|
+
}
|
4520
|
+
obj.append(jQuery("<select id='voiceSelect' name='voiceSelect'><option value='none'>" + selectionTxt + "</option></select>"));
|
4521
|
+
|
4522
|
+
// jadams
|
4523
|
+
var skippedVoices = 0;
|
4524
|
+
for (var i = 0; i < voices.length; i++) {
|
4525
|
+
if (isChrome && voices[i].name.includes(ignoreProvider)) {
|
4526
|
+
skippedVoices++;
|
4527
|
+
continue;
|
4528
|
+
}
|
4529
|
+
if (isEdge && !voices[i].name.includes('Natural')) {
|
4530
|
+
skippedVoices++;
|
4531
|
+
continue;
|
4532
|
+
}
|
4533
|
+
var option = document.createElement('option');
|
4534
|
+
option.textContent = voices[i].name + ' (' + voices[i].language + ')';
|
4535
|
+
option.setAttribute('value', voices[i].name);
|
4536
|
+
|
4537
|
+
// set used voice as 'selected'
|
4538
|
+
if (voiceLanguageDefault !== undefined) {
|
4539
|
+
if (voices[i].name === voiceLanguageDefault) {
|
4540
|
+
option.setAttribute('selected', 'selected');
|
4541
|
+
}
|
4542
|
+
} else {
|
4543
|
+
if (voices[i].name.includes(voiceUserDefault)) {
|
4544
|
+
// option.setAttribute('selected', 'selected');
|
4545
|
+
}
|
4546
|
+
}
|
4547
|
+
option.setAttribute('data-speak2me-language', voices[i].language);
|
4548
|
+
obj.find('select').append(option);
|
4549
|
+
}
|
4550
|
+
return i - skippedVoices;
|
4551
|
+
},
|
4552
|
+
// END get Voiuces
|
4553
|
+
|
4554
|
+
setVoice: function () {
|
4555
|
+
// The setVoice function has to have two attributes
|
4556
|
+
// if not, exit the function.
|
4557
|
+
//
|
4558
|
+
if (arguments.length < 2) {
|
4559
|
+
return this;
|
4560
|
+
}
|
4561
|
+
var requestedVoice, requestedLanguage;
|
4562
|
+
|
4563
|
+
// User wants to change the voice directly. If that name indeed
|
4564
|
+
// exists, update the "voiceUserDefault" variable.
|
4565
|
+
//
|
4566
|
+
if (arguments[0] == 'name') {
|
4567
|
+
requestedVoice = arguments[1];
|
4568
|
+
for (var i = 0; i < voices.length; i++) {
|
4569
|
+
if (voices[i].name == requestedVoice) {
|
4570
|
+
voiceUserDefault = requestedVoice;
|
4571
|
+
}
|
4572
|
+
}
|
4573
|
+
}
|
4574
|
+
|
4575
|
+
// User wants to change the voice by only specifying the
|
4576
|
+
// first two characters of the language code. Case insensitive.
|
4577
|
+
//
|
4578
|
+
if (arguments[0] == 'language') {
|
4579
|
+
requestedLanguage = arguments[1].toUpperCase();
|
4580
|
+
if (requestedLanguage.length == 2) {
|
4581
|
+
for (var i = 0; i < voices.length; i++) {
|
4582
|
+
if (voices[i].language.substring(0, 2).toUpperCase() == requestedLanguage) {
|
4583
|
+
voiceUserDefault = voices[i].name;
|
4584
|
+
break;
|
4585
|
+
}
|
4586
|
+
}
|
4587
|
+
} else {
|
4588
|
+
// User wants to change the voice by specifying the
|
4589
|
+
// complete language code.
|
4590
|
+
for (var i = 0; i < voices.length; i++) {
|
4591
|
+
if (voices[i].language == requestedLanguage) {
|
4592
|
+
voiceUserDefault = voices[i].name;
|
4593
|
+
break;
|
4594
|
+
}
|
4595
|
+
}
|
4596
|
+
}
|
4597
|
+
}
|
4598
|
+
return this;
|
4599
|
+
} // END set Voice
|
4600
|
+
}; // END methods
|
4601
|
+
|
4602
|
+
// main speak2me method
|
4603
|
+
//
|
4604
|
+
$.fn.speak2me = function (method) {
|
4605
|
+
if (methods[method]) {
|
4606
|
+
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
4607
|
+
} else if (typeof method === 'object' || !method) {
|
4608
|
+
return methods.speak.apply(this, arguments);
|
4609
|
+
} else {
|
4610
|
+
jQuery.error('Method ' + method + ' does not exist on jQuery.speak2me');
|
4611
|
+
}
|
4612
|
+
};
|
4613
|
+
})(jQuery);
|
4614
|
+
|
4615
|
+
/***/ }),
|
4616
|
+
|
2960
4617
|
/***/ 525:
|
2961
4618
|
/***/ ((module) => {
|
2962
4619
|
|
@@ -9871,6 +11528,7 @@ window.j1.core.scrollSmooth = __webpack_require__(814);
|
|
9871
11528
|
|
9872
11529
|
const J1Tocbot = __webpack_require__(799);
|
9873
11530
|
const J1AttrChangeListener = __webpack_require__(610);
|
11531
|
+
const J1Speak2Me = __webpack_require__(229);
|
9874
11532
|
|
9875
11533
|
// Passing log data over Internet|SeeMe (currently NOT used)
|
9876
11534
|
// -----------------------------------------------------------------------------
|