diff options
Diffstat (limited to 'ext/fg/js/document.js')
| -rw-r--r-- | ext/fg/js/document.js | 162 | 
1 files changed, 162 insertions, 0 deletions
| diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js new file mode 100644 index 00000000..582b6770 --- /dev/null +++ b/ext/fg/js/document.js @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016  Alex Yatskov <alex@foosoft.net> + * Author: Alex Yatskov <alex@foosoft.net> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + + +function docOffsetCalc(element) { +    const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; +    const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; + +    const clientTop = document.documentElement.clientTop || document.body.clientTop || 0; +    const clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0; + +    const rect = element.getBoundingClientRect(); +    const top  = Math.round(rect.top +  scrollTop - clientTop); +    const left = Math.round(rect.left + scrollLeft - clientLeft); + +    return {top, left}; +} + +function docImposterCreate(element) { +    const styleProps = window.getComputedStyle(element); +    const stylePairs = []; +    for (const key of styleProps) { +        stylePairs.push(`${key}: ${styleProps[key]};`); +    } + +    const offset = docOffsetCalc(element); +    const imposter = document.createElement('div'); +    imposter.className = 'yomichan-imposter'; +    imposter.innerText = element.value; +    imposter.style.cssText = stylePairs.join('\n'); +    imposter.style.position = 'absolute'; +    imposter.style.top = `${offset.top}px`; +    imposter.style.left = `${offset.left}px`; +    imposter.style.zIndex = 2147483646; +    if (element.nodeName === 'TEXTAREA' && styleProps.overflow === 'visible') { +        imposter.style.overflow = 'auto'; +    } + +    document.body.appendChild(imposter); +    imposter.scrollTop = element.scrollTop; +    imposter.scrollLeft = element.scrollLeft; +} + +function docImposterDestroy() { +    for (const element of document.getElementsByClassName('yomichan-imposter')) { +        element.parentNode.removeChild(element); +    } +} + +function docRangeFromPoint(point) { +    const element = document.elementFromPoint(point.x, point.y); +    if (element) { +        if (element.nodeName === 'IMG' || element.nodeName === 'BUTTON') { +            return new TextSourceElement(element); +        } else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { +            docImposterCreate(element); +        } +    } + +    if (!document.caretRangeFromPoint) { +        document.caretRangeFromPoint = (x, y) => { +            const position = document.caretPositionFromPoint(x,y); +            if (position) { +                const range = document.createRange(); +                range.setStart(position.offsetNode, position.offset); +                range.setEnd(position.offsetNode, position.offset); +                return range; +            } +        }; +    } + +    const range = document.caretRangeFromPoint(point.x, point.y); +    if (range) { +        return new TextSourceRange(range); +    } +} + +function docSentenceExtract(source, extent) { +    const quotesFwd = {'「': '」', '『': '』', "'": "'", '"': '"'}; +    const quotesBwd = {'」': '「', '』': '『', "'": "'", '"': '"'}; +    const terminators = '…。..??!!'; + +    const sourceLocal = source.clone(); +    const position = sourceLocal.setStartOffset(extent); +    sourceLocal.setEndOffset(position + extent); +    const content = sourceLocal.text(); + +    let quoteStack = []; + +    let startPos = 0; +    for (let i = position; i >= startPos; --i) { +        const c = content[i]; + +        if (c === '\n') { +            startPos = i + 1; +            break; +        } + +        if (quoteStack.length === 0 && (terminators.includes(c) || c in quotesFwd)) { +            startPos = i + 1; +            break; +        } + +        if (quoteStack.length > 0 && c === quoteStack[0]) { +            quoteStack.pop(); +        } else if (c in quotesBwd) { +            quoteStack = [quotesBwd[c]].concat(quoteStack); +        } +    } + +    quoteStack = []; + +    let endPos = content.length; +    for (let i = position; i <= endPos; ++i) { +        const c = content[i]; + +        if (c === '\n') { +            endPos = i + 1; +            break; +        } + +        if (quoteStack.length === 0) { +            if (terminators.includes(c)) { +                endPos = i + 1; +                break; +            } +            else if (c in quotesBwd) { +                endPos = i; +                break; +            } +        } + +        if (quoteStack.length > 0 && c === quoteStack[0]) { +            quoteStack.pop(); +        } else if (c in quotesFwd) { +            quoteStack = [quotesFwd[c]].concat(quoteStack); +        } +    } + +    const text = content.substring(startPos, endPos); +    const padding = text.length - text.replace(/^\s+/, '').length; + +    return { +        text: text.trim(), +        offset: position - startPos - padding +    }; +} |