UNPKG

wbf

Version:

[![LICENSE](https://img.shields.io/github/license/halodong/web-barrier-free?style=flat-square)](./LICENSE) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/halodong/web-barrier

359 lines (346 loc) 13.8 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Wbf = factory()); })(this, (function () { 'use strict'; const defaultOptions = { language: 'zh-CN', rate: 1, pitch: 1, volume: 1 }; const showBarDomId = '$$wsashowbar'; const consoleDomId = '$$wsaConsole'; const emphasizeClassName = 'emphasizeStyle'; const consoleClassName = 'consoleEl'; const optionsArr = ['language', 'rate', 'pitch', 'volume']; const cnGather = { a: '链接', img: '图片', nav: '链接', close: '关闭', continuousRead: '连读', fingerRead: '指读', volume: '音量', rate: '语速' }; const enGather = { a: 'Link', img: 'Image', nav: 'Link', close: 'Close', continuousRead: 'ContinuousRead', fingerRead: 'FingerRead', volume: 'Volume', rate: 'Rate' }; const lowerCaseImgTagName = 'img'; const modes = ['finger', 'continuous']; const getGather = (language) => { let gather = cnGather; if (language === 'en') { gather = enGather; } return gather; }; const getElText = (el, language) => { const tag = descriptionTag(el.tagName, language); const notContainChildText = getNotContainChildText(el); const text = tag !== null ? `${tag}: ${notContainChildText}` : notContainChildText; return text; }; const descriptionTag = (tagName, language) => { const tag = tagName.toLowerCase(); const gather = getGather(language); return gather[tag] !== undefined ? gather[tag] : null; }; const getNotContainChildText = (el) => { if (el.tagName.toLowerCase() === lowerCaseImgTagName) { return el.alt; } const notContainChildText = Array.prototype.filter .call(el.childNodes, (node) => node.nodeType === 3) .map((node) => node.nodeValue.trim()) .join(''); return notContainChildText; }; const testReadMode = (mode) => { return Array.prototype.includes.call(modes, mode); }; const overHandler = (e, wbf) => { const notContainChildText = getNotContainChildText(e.target); if (notContainChildText.length === 0) return; if (e.target !== document.body && e.target.tagName.toLowerCase() !== 'html' && e.target.id !== showBarDomId) { wbf.emphasize(e.target); const text = getElText(e.target, wbf.language); wbf.readMode === 'finger' && wbf.playAudio(text); if (wbf.showBarEl != null) { if (text.length > 150) { wbf.showBarEl.style.fontSize = '24px'; } else if (text.length > 100) { wbf.showBarEl.style.fontSize = '26px'; } else if (text.length > 50) { wbf.showBarEl.style.fontSize = '28px'; } else if (text.length > 20) { wbf.showBarEl.style.fontSize = '30px'; } else { wbf.showBarEl.style.fontSize = '32px'; } wbf.showBarEl.innerText = text; } } }; const outHandler = (e, wbf) => { wbf.removeEmphasize(e.target); }; function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z = ".emphasizeStyle {\r\n background: black !important;\r\n color: white !important;\r\n}\r\n\r\n.consoleEl {\r\n padding: 10px;\r\n z-index: 9999;\r\n border-bottom: 1px #2f5bb7 solid;\r\n}\r\n\r\n.consoleEl .consoleEl-main {\r\n display: flex;\r\n justify-content: space-around;\r\n flex-wrap: wrap;\r\n margin: 0 auto;\r\n width: 50vw;\r\n}\r\n@media (max-width: 750px) {\r\n .consoleEl .consoleEl-main {\r\n width: 100vw;\r\n }\r\n}\r\n.consoleEl button {\r\n color: white;\r\n background: #4c8ffb;\r\n padding: 5px 10px;\r\n border-radius: 2px;\r\n font-weight: bold;\r\n font-size: 9pt;\r\n outline: none;\r\n box-shadow: inset 0 1px 0 #80b0fb;\r\n}\r\n\r\n.consoleEl button:hover {\r\n border: 1px #2f5bb7 solid;\r\n box-shadow: 0 1px 1px #eaeaea, inset 0 1px 0 #5a94f1;\r\n background: #3f83f1;\r\n}\r\n\r\n.consoleEl #_wbfClose {\r\n border: 1px #f0120b solid;\r\n background-color: #d1341f;\r\n}\r\n.consoleEl #_wbfClose:hover {\r\n background-color: #ad2210;\r\n}\r\n"; styleInject(css_248z); let instance = null; let compatible = true; class Wbf { opening = false; readMode = 'finger'; language; rate; pitch; volume; showBarEl = null; needConsole = true; overHandler; outHandler; static getInstance = (options) => { if (instance === null) { instance = new Wbf(options); } return instance; }; static clearInstance = () => { instance = null; }; constructor(options) { if (typeof SpeechSynthesisUtterance === 'undefined' || typeof speechSynthesis === 'undefined') { compatible = false; console.warn('Current browser does not support SpeechSynthesisUtterance and speechSynthesis'); } if (options == null) options = defaultOptions; options?.readMode !== undefined && (this.readMode = options.readMode); options?.needConsole !== undefined && (this.needConsole = options.needConsole); this.language = options?.language ?? defaultOptions.language; this.rate = options?.rate ?? defaultOptions.rate; this.pitch = options?.pitch ?? defaultOptions.pitch; this.volume = options?.volume ?? defaultOptions.volume; this.overHandler = (e) => overHandler(e, this); this.outHandler = (e) => outHandler(e, this); if (instance != null) { instance = this; console.warn('There are currently multiple wbf instances'); } } open() { if (this.opening) return; this.changeMode(this.readMode); if (this.showBarEl == null) { const showBar = this.createShowBarDom(); this.showBarEl = showBar; } this.addHandler(); this.opening = true; this.needConsole && this.createConsole(); } close() { const emphasizeEls = document.querySelectorAll(`.${emphasizeClassName}`); emphasizeEls.forEach((el) => { this.removeEmphasize(el); }); compatible && window.speechSynthesis?.cancel(); document.removeEventListener('mouseover', this.overHandler); document.removeEventListener('mouseout', this.outHandler); this.removeShowBarDom(); this.removeConsole(); this.opening = false; } setOption(keyName, value) { if (optionsArr.includes[keyName] === false && this[keyName] !== undefined) { throw new Error(`${keyName} options do not exist on wbf`); } if (keyName === 'opening') throw new Error(`${keyName} cannot be changed `); switch (keyName) { case 'rate': if (value > 10) value = 10; if (value < 0.1) value = 0.1; break; case 'pitch': if (value > 2) value = 2; if (value < 0) value = 0; break; case 'volume': if (value > 1) value = 1; if (value < 0) value = 0; break; } this[keyName] = value; } changeMode(readMode) { if (!testReadMode(readMode)) { throw new Error(`readMode not includes this ${readMode}`); } this.readMode = readMode; if (readMode !== 'finger') { const allText = document.body.innerText; this.playAudio(allText); } } playAudio(str) { if (!compatible) return; window.speechSynthesis?.cancel(); const msg = this.createUtterance(str); window.speechSynthesis?.speak(msg); return msg; } emphasize(el) { el.classList.add(emphasizeClassName); } removeEmphasize(el) { el.classList.remove(emphasizeClassName); } addHandler() { document.addEventListener('mouseover', this.overHandler); document.addEventListener('mouseout', this.outHandler); } createUtterance(str) { if (!compatible) return; const msg = new SpeechSynthesisUtterance(); msg.text = str; msg.lang = this.language; msg.pitch = this.pitch; msg.rate = this.rate; msg.volume = this.volume; return msg; } createShowBarDom() { const prev = document.getElementById(showBarDomId); if (prev != null) return prev; const showBar = document.createElement('div'); showBar.id = showBarDomId; showBar.style.position = 'fixed'; showBar.style.bottom = '0px'; showBar.style.left = '0px'; showBar.style.width = '100%'; showBar.style.minHeight = '50px'; showBar.style.maxHeight = '300px'; showBar.style.fontWeight = 'bold'; showBar.style.textAlign = 'center'; showBar.style.wordBreak = 'break-word;'; showBar.style.overflow = 'hidden'; showBar.style.background = 'white'; showBar.style.border = '2px solid #eee'; document.body.appendChild(showBar); return showBar; } createConsole() { const prev = document.getElementById(consoleDomId); if (prev != null) return; const consoleEl = document.createElement('div'); consoleEl.id = consoleDomId; const gather = getGather(this.language); consoleEl.classList.add(consoleClassName); consoleEl.innerHTML = ` <div class="${consoleClassName}-main"> <div> <button id="_wbfClose">${gather.close}</button> <button id="_wbfContinuousRead">${gather.continuousRead}</button> <button id="_wbfFingerRead">${gather.fingerRead}</button> </div> | <div> ${gather.volume} <button id="_wbfAddVolume">+</button> <button id="_wbfReduceVolume">-</button> </div> | <div> ${gather.rate} <button id="_wbfAddRate">+</button> <button id="_wbfReduceRate">-</button> </div> </div>`; document.body.insertBefore(consoleEl, document.body.firstChild); const closeBtn = document.getElementById('_wbfClose'); const continuousReadBtn = document.getElementById('_wbfContinuousRead'); const fingerReadBtn = document.getElementById('_wbfFingerRead'); const addVolumeBtn = document.getElementById('_wbfAddVolume'); const reduceVolumeBtn = document.getElementById('_wbfReduceVolume'); const addRateBtn = document.getElementById('_wbfAddRate'); const reduceRateBtn = document.getElementById('_wbfReduceRate'); closeBtn != null && (closeBtn.onclick = () => this.close()); continuousReadBtn != null && (continuousReadBtn.onclick = () => this.changeMode('continuous')); fingerReadBtn != null && (fingerReadBtn.onclick = () => this.changeMode('finger')); addVolumeBtn != null && (addVolumeBtn.onclick = () => this.setOption('volume', this.volume + 0.1)); reduceVolumeBtn != null && (reduceVolumeBtn.onclick = () => this.setOption('volume', this.volume - 0.1)); addRateBtn != null && (addRateBtn.onclick = () => this.setOption('rate', this.rate + 0.1)); reduceRateBtn != null && (reduceRateBtn.onclick = () => this.setOption('rate', this.rate - 0.1)); } removeConsole() { const consoleEl = document.getElementById(consoleDomId); if (consoleEl != null) { consoleEl.remove(); } } removeShowBarDom() { if (this.showBarEl != null) { this.showBarEl.remove(); this.showBarEl = null; } } } return Wbf; })); //# sourceMappingURL=index.js.map