diff options
| -rw-r--r-- | ext/bg/js/options.js | 3 | ||||
| -rw-r--r-- | ext/bg/js/settings.js | 6 | ||||
| -rw-r--r-- | ext/bg/settings.html | 19 | ||||
| -rw-r--r-- | ext/fg/css/client.css | 1 | ||||
| -rw-r--r-- | ext/fg/js/frontend.js | 8 | ||||
| -rw-r--r-- | ext/fg/js/popup.js | 147 | ||||
| -rw-r--r-- | ext/fg/js/source.js | 25 | 
7 files changed, 170 insertions, 39 deletions
| diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 29d8a215..c4a7e681 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -199,6 +199,9 @@ function optionsSetDefaults(options) {              popupHeight: 250,              popupHorizontalOffset: 0,              popupVerticalOffset: 10, +            popupHorizontalOffset2: 10, +            popupVerticalOffset2: 0, +            popupVerticalTextPosition: 'before',              showGuide: true,              compactTags: false,              compactGlossaries: false, diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index cdcdae77..83a1c2f6 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -32,10 +32,13 @@ async function formRead() {      optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked');      optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10);      optionsNew.general.popupDisplayMode = $('#popup-display-mode').val(); +    optionsNew.general.popupVerticalTextPosition = $('#popup-vertical-text-position').val();      optionsNew.general.popupWidth = parseInt($('#popup-width').val(), 10);      optionsNew.general.popupHeight = parseInt($('#popup-height').val(), 10);      optionsNew.general.popupHorizontalOffset = parseInt($('#popup-horizontal-offset').val(), 0);      optionsNew.general.popupVerticalOffset = parseInt($('#popup-vertical-offset').val(), 10); +    optionsNew.general.popupHorizontalOffset2 = parseInt($('#popup-horizontal-offset2').val(), 0); +    optionsNew.general.popupVerticalOffset2 = parseInt($('#popup-vertical-offset2').val(), 10);      optionsNew.general.customPopupCss = $('#custom-popup-css').val();      optionsNew.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked'); @@ -168,10 +171,13 @@ async function onReady() {      $('#show-advanced-options').prop('checked', options.general.showAdvanced);      $('#max-displayed-results').val(options.general.maxResults);      $('#popup-display-mode').val(options.general.popupDisplayMode); +    $('#popup-vertical-text-position').val(options.general.popupVerticalTextPosition);      $('#popup-width').val(options.general.popupWidth);      $('#popup-height').val(options.general.popupHeight);      $('#popup-horizontal-offset').val(options.general.popupHorizontalOffset);      $('#popup-vertical-offset').val(options.general.popupVerticalOffset); +    $('#popup-horizontal-offset2').val(options.general.popupHorizontalOffset2); +    $('#popup-vertical-offset2').val(options.general.popupVerticalOffset2);      $('#custom-popup-css').val(options.general.customPopupCss);      $('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 0704140e..c6090c91 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -107,6 +107,17 @@                      </select>                  </div> +                <div class="form-group"> +                    <label for="popup-display-mode">Popup position for vertical text</label> +                    <select class="form-control" id="popup-vertical-text-position"> +                        <option value="default">Same as for horizontal text</option> +                        <option value="before">Before text reading direction</option> +                        <option value="after">After text reading direction</option> +                        <option value="left">Left of text</option> +                        <option value="right">Right of text</option> +                    </select> +                </div> +                  <div class="form-group options-advanced">                      <label for="audio-playback-volume">Audio playback volume (percent)</label>                      <input type="number" min="0" max="100" id="audio-playback-volume" class="form-control"> @@ -134,6 +145,14 @@                  </div>                  <div class="form-group options-advanced"> +                    <label>Popup offset for vertical text (horizontal, vertical; in pixels)</label> +                    <div class="row"> +                        <div class="col-xs-6"><input type="number" min="0" id="popup-horizontal-offset2" class="form-control"></div> +                        <div class="col-xs-6"><input type="number" min="0" id="popup-vertical-offset2" class="form-control"></div> +                    </div> +                </div> + +                <div class="form-group options-advanced">                      <label for="custom-popup-css">Custom popup CSS</label>                      <div><textarea autocomplete="off" spellcheck="false" wrap="soft" id="custom-popup-css" class="form-control"></textarea></div>                  </div> diff --git a/ext/fg/css/client.css b/ext/fg/css/client.css index a9b8e025..a2b06d0f 100644 --- a/ext/fg/css/client.css +++ b/ext/fg/css/client.css @@ -26,6 +26,7 @@ iframe#yomichan-float {      resize: both;      visibility: hidden;      z-index: 2147483647; +    box-sizing: border-box;  }  iframe#yomichan-float.yomichan-float-full-width { diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 3c5f2ac8..72379062 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -301,7 +301,11 @@ class Frontend {          } catch (e) {              if (window.yomichan_orphaned) {                  if (textSource && this.options.scanning.modifier !== 'none') { -                    this.popup.showOrphaned(textSource.getRect(), this.options); +                    this.popup.showOrphaned( +                        textSource.getRect(), +                        textSource.getWritingMode(), +                        this.options +                    );                  }              } else {                  this.onError(e); @@ -332,6 +336,7 @@ class Frontend {          const url = window.location.href;          this.popup.termsShow(              textSource.getRect(), +            textSource.getWritingMode(),              definitions,              this.options,              {sentence, url, focus} @@ -357,6 +362,7 @@ class Frontend {          const url = window.location.href;          this.popup.kanjiShow(              textSource.getRect(), +            textSource.getWritingMode(),              definitions,              this.options,              {sentence, url, focus} diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 18dc0386..138dec41 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -48,59 +48,130 @@ class Popup {          return this.injected;      } -    async show(elementRect, options) { +    async show(elementRect, writingMode, options) {          await this.inject(options); -        const containerStyle = window.getComputedStyle(this.container); -        const containerHeight = parseInt(containerStyle.height); -        const containerWidth = parseInt(containerStyle.width); +        const optionsGeneral = options.general; +        const container = this.container; +        const containerRect = container.getBoundingClientRect(); +        const getPosition = ( +            writingMode === 'horizontal-tb' || optionsGeneral.popupVerticalTextPosition === 'default' ? +            Popup.getPositionForHorizontalText : +            Popup.getPositionForVerticalText +        ); -        const limitX = document.body.clientWidth; -        const limitY = window.innerHeight; +        const [x, y, width, height, below] = getPosition( +            elementRect, +            Math.max(containerRect.width, optionsGeneral.popupWidth), +            Math.max(containerRect.height, optionsGeneral.popupHeight), +            document.body.clientWidth, +            window.innerHeight, +            optionsGeneral, +            writingMode +        ); -        let x = elementRect.left + options.general.popupHorizontalOffset; -        let width = Math.max(containerWidth, options.general.popupWidth); -        const overflowX = Math.max(x + width - limitX, 0); +        container.classList.toggle('yomichan-float-full-width', optionsGeneral.popupDisplayMode === 'full-width'); +        container.classList.toggle('yomichan-float-above', !below); +        container.style.left = `${x}px`; +        container.style.top = `${y}px`; +        container.style.width = `${width}px`; +        container.style.height = `${height}px`; +        container.style.visibility = 'visible'; +    } + +    static getPositionForHorizontalText(elementRect, width, height, maxWidth, maxHeight, optionsGeneral) { +        let x = elementRect.left + optionsGeneral.popupHorizontalOffset; +        const overflowX = Math.max(x + width - maxWidth, 0);          if (overflowX > 0) {              if (x >= overflowX) {                  x -= overflowX;              } else { -                width = limitX; +                width = maxWidth;                  x = 0;              }          } -        let above = false; -        let y = 0; -        let height = Math.max(containerHeight, options.general.popupHeight); -        const yBelow = elementRect.bottom + options.general.popupVerticalOffset; -        const yAbove = elementRect.top - options.general.popupVerticalOffset; -        const overflowBelow = Math.max(yBelow + height - limitY, 0); -        const overflowAbove = Math.max(height - yAbove, 0); -        if (overflowBelow > 0 || overflowAbove > 0) { -            if (overflowBelow < overflowAbove) { -                height = Math.max(height - overflowBelow, 0); -                y = yBelow; +        const verticalOffset = optionsGeneral.popupVerticalOffset; +        const [y, h, below] = Popup.limitGeometry( +            elementRect.top - verticalOffset, +            elementRect.bottom + verticalOffset, +            height, +            maxHeight, +            true +        ); + +        return [x, y, width, h, below]; +    } + +    static getPositionForVerticalText(elementRect, width, height, maxWidth, maxHeight, optionsGeneral, writingMode) { +        const preferRight = Popup.isVerticalTextPopupOnRight(optionsGeneral.popupVerticalTextPosition, writingMode); +        const horizontalOffset = optionsGeneral.popupHorizontalOffset2; +        const verticalOffset = optionsGeneral.popupVerticalOffset2; + +        const [x, w] = Popup.limitGeometry( +            elementRect.left - horizontalOffset, +            elementRect.right + horizontalOffset, +            width, +            maxWidth, +            preferRight +        ); +        const [y, h, below] = Popup.limitGeometry( +            elementRect.bottom - verticalOffset, +            elementRect.top + verticalOffset, +            height, +            maxHeight, +            true +        ); +        return [x, y, w, h, below]; +    } + +    static isVerticalTextPopupOnRight(positionPreference, writingMode) { +        switch (positionPreference) { +            case 'before': +                return !Popup.isWritingModeLeftToRight(writingMode); +            case 'after': +                return Popup.isWritingModeLeftToRight(writingMode); +            case 'left': +                return false; +            case 'right': +                return true; +        } +    } + +    static isWritingModeLeftToRight(writingMode) { +        switch (writingMode) { +            case 'vertical-lr': +            case 'sideways-lr': +                return true; +            default: +                return false; +        } +    } + +    static limitGeometry(positionBefore, positionAfter, size, limit, preferAfter) { +        let after = preferAfter; +        let position = 0; +        const overflowBefore = Math.max(0, size - positionBefore); +        const overflowAfter = Math.max(0, positionAfter + size - limit); +        if (overflowAfter > 0 || overflowBefore > 0) { +            if (overflowAfter < overflowBefore) { +                size = Math.max(0, size - overflowAfter); +                position = positionAfter; +                after = true;              } else { -                height = Math.max(height - overflowAbove, 0); -                y = Math.max(yAbove - height, 0); -                above = true; +                size = Math.max(0, size - overflowBefore); +                position = Math.max(0, positionBefore - size); +                after = false;              }          } else { -            y = yBelow; +            position = preferAfter ? positionAfter : positionBefore - size;          } -        this.container.classList.toggle('yomichan-float-full-width', options.general.popupDisplayMode === 'full-width'); -        this.container.classList.toggle('yomichan-float-above', above); -        this.container.style.left = `${x}px`; -        this.container.style.top = `${y}px`; -        this.container.style.width = `${width}px`; -        this.container.style.height = `${height}px`; -        this.container.style.visibility = 'visible'; +        return [position, size, after];      } -    async showOrphaned(elementRect, options) { -        await this.show(elementRect, options); +    async showOrphaned(elementRect, writingMode, options) { +        await this.show(elementRect, writingMode, options);          this.invokeApi('orphaned');      } @@ -136,13 +207,13 @@ class Popup {          return contained;      } -    async termsShow(elementRect, definitions, options, context) { -        await this.show(elementRect, options); +    async termsShow(elementRect, writingMode, definitions, options, context) { +        await this.show(elementRect, writingMode, options);          this.invokeApi('termsShow', {definitions, options, context});      } -    async kanjiShow(elementRect, definitions, options, context) { -        await this.show(elementRect, options); +    async kanjiShow(elementRect, writingMode, definitions, options, context) { +        await this.show(elementRect, writingMode, options);          this.invokeApi('kanjiShow', {definitions, options, context});      } diff --git a/ext/fg/js/source.js b/ext/fg/js/source.js index a360b331..a5421934 100644 --- a/ext/fg/js/source.js +++ b/ext/fg/js/source.js @@ -61,6 +61,10 @@ class TextSourceRange {          return this.range.getBoundingClientRect();      } +    getWritingMode() { +        return TextSourceRange.getElementWritingMode(TextSourceRange.getParentElement(this.range.startContainer)); +    } +      getPaddedRect() {          const range = this.range.cloneRange();          const startOffset = range.startOffset; @@ -204,6 +208,23 @@ class TextSourceRange {          return state.remainder > 0;      } + +    static getParentElement(node) { +        while (node !== null && node.nodeType !== Node.ELEMENT_NODE) { +            node = node.parentNode; +        } +        return node; +    } + +    static getElementWritingMode(element) { +        if (element === null) { +            return 'horizontal-tb'; +        } + +        const style = window.getComputedStyle(element); +        const writingMode = style.writingMode; +        return typeof writingMode === 'string' ? writingMode : 'horizontal-tb'; +    }  } @@ -267,6 +288,10 @@ class TextSourceElement {          return this.element.getBoundingClientRect();      } +    getWritingMode() { +        return 'horizontal-tb'; +    } +      select() {          // NOP      } |