// Line and character counting function // // // ## 入力データ // //
//
...
//

...

//
// // `div.speech > p`の中にはテキストあるいはト書きのspan要素が入っている。 // これらの文字数を行数を数える。このとき、ruby要素に関してはルビは無視する。 // 行数は行の高さでp要素の高さを割って切り上げる。 // 一番上の行は行の高さいっぱいまで使わない。 // /// Returns the inner paragraph of `div.speech` function stripSpeech(sp) { const p = sp.querySelector('p'); return p; } /// Counts characters in an inner paragraph of `div.speech` function countCharacters(p) { let count = 0; for ( node of p.childNodes ) { count += countCharsInSpan(node); } return count; } function countCharsInSpan(span) { let count = 0; for ( node of span.childNodes ) { if ( node instanceof Text ) { count += node.length; } else if ( node.tagName === "RUBY" ) { count += countCharsInRuby(node); } } return count; } /// Counts the number of base characters in the given RUBY element. function countCharsInRuby(ruby) { let count = 0; for ( node of ruby.childNodes ) { if ( node instanceof Text ) { count += node.length; } } return count; } /// Gets a class name starts with `scene` function getSceneClassName(elem) { for ( className of elem.classList.values() ) { if ( className.startsWith('scene') ) { return className; } } } function countCharactersInSpeeches(holderMap) { const script = document.querySelectorAll('div.speech'); const lineCountMap = new Map(); const charCountMap = new Map(); let characters = 0; let lines = 0; for ( speech of script ) { const sceneName = getSceneClassName(speech); const p = stripSpeech(speech); const pChars = countCharacters(p); if ( charCountMap.has(sceneName) ) { const chars = charCountMap.get(sceneName); charCountMap.set(sceneName, chars + pChars); } else { charCountMap.set(sceneName, pChars); } const pHeight = p.offsetHeight; const style = window.getComputedStyle(p); const lineHeight = parseFloat(style.getPropertyValue('line-height')); const pLines = Math.ceil(pHeight / lineHeight); if ( lineCountMap.has(sceneName) ) { const lines = lineCountMap.get(sceneName); lineCountMap.set(sceneName, lines + pLines); } else { lineCountMap.set(sceneName, pLines); } } for ( pair of holderMap.entries() ) { const lines = lineCountMap.get(pair[0]); const characters = charCountMap.get(pair[0]); pair[1].innerText = lines + '行, ' + characters + '字'; } } const holders = document.getElementsByClassName('mdplayscript-count'); // a mapping from scene class name to place holder element const holderMap = new Map(); for ( holder of holders ) { for ( className of holder.classList.values() ) { if ( className.startsWith('scene') ) { holderMap.set(className, holder); } } } console.log(holderMap); countCharactersInSpeeches(holderMap);