Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typescript version #64

Open
eggp opened this issue Sep 27, 2021 · 0 comments
Open

typescript version #64

eggp opened this issue Sep 27, 2021 · 0 comments

Comments

@eggp
Copy link

eggp commented Sep 27, 2021

/* Detect-zoom
 * -----------
 * Cross Browser Zoom and Pixel Ratio Detector
 * Version 1.0.4 | Apr 1 2013
 * dual-licensed under the WTFPL and MIT license
 * Maintained by https://github/tombigel
 * Original developer https://github.com/yonran
 */

export class DetectZoom {
  constructor(private window: Window) {}
  /**
   * Use devicePixelRatio if supported by the browser
   */
  get devicePixelRatio(): number {
    return this.window.devicePixelRatio || 1;
  }

  /**
   * Fallback function to set default values
   */
  private fallback() {
    return {
      zoom: 1,
      devicePxPerCssPx: 1,
    };
  }

  /**
   * IE 8 and 9: no trick needed!
   */
  private ie8() {
    const zoom = Math.round(((this.window.screen as any).deviceXDPI / (this.window.screen as any).logicalXDPI) * 100) / 100;
    return {
      zoom: zoom,
      devicePxPerCssPx: zoom * this.devicePixelRatio,
    };
  }

  /**
   * For IE10 we need to change our technique again...
   * thanks https://github.com/stefanvanburen
   */
  private ie10() {
    const zoom = Math.round((this.window.document.documentElement.offsetHeight / this.window.innerHeight) * 100) / 100;
    return {
      zoom: zoom,
      devicePxPerCssPx: zoom * this.devicePixelRatio,
    };
  }

  /**
   * For chrome
   */
  private chrome() {
    const zoom = Math.round((this.window.outerWidth / this.window.innerWidth) * 100) / 100;
    return {
      zoom: zoom,
      devicePxPerCssPx: zoom * this.devicePixelRatio,
    };
  }

  /**
   * For safari (same as chrome)
   */
  private safari() {
    const zoom = Math.round((this.window.document.documentElement.clientWidth / this.window.innerWidth) * 100) / 100;
    return {
      zoom: zoom,
      devicePxPerCssPx: zoom * this.devicePixelRatio,
    };
  }

  /**
   * Mobile WebKit
   * the trick: window.innerWIdth is in CSS pixels, while
   * screen.width and screen.height are in system pixels.
   * And there are no scrollbars to mess up the measurement.
   */
  private webkitMobile() {
    const deviceWidth = Math.abs(this.window.screen.orientation.angle) === 90 ? screen.height : screen.width;
    const zoom = deviceWidth / window.innerWidth;
    return {
      zoom: zoom,
      devicePxPerCssPx: zoom * this.devicePixelRatio,
    };
  }

  /**
   * Desktop Webkit
   * the trick: an element's clientHeight is in CSS pixels, while you can
   * set its line-height in system pixels using font-size and
   * -webkit-text-size-adjust:none.
   * device-pixel-ratio: http://www.webkit.org/blog/55/high-dpi-web-sites/
   *
   * Previous trick (used before http://trac.webkit.org/changeset/100847):
   * documentElement.scrollWidth is in CSS pixels, while
   * document.width was in system pixels. Note that this is the
   * layout width of the document, which is slightly different from viewport
   * because document width does not include scrollbars and might be wider
   * due to big elements.
   */
  private webkit() {
    const important = str => str.replace(/;/g, ' !important;');

    const div = this.window.document.createElement('div');
    div.innerHTML = '1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>0';
    div.setAttribute(
      'style',
      important(
        'font: 100px/1em sans-serif; -webkit-text-size-adjust: none; text-size-adjust: none; height: auto; width: 1em; padding: 0; overflow: visible;'
      )
    );

    // The container exists so that the div will be laid out in its own flow
    // while not impacting the layout, viewport size, or display of the
    // webpage as a whole.
    // Add !important and relevant CSS rule resets
    // so that other rules cannot affect the results.
    const container = this.window.document.createElement('div');
    container.setAttribute('style', important('width:0; height:0; overflow:hidden; visibility:hidden; position: absolute;'));
    container.appendChild(div);

    document.body.appendChild(container);
    let zoom = 1000 / div.clientHeight;
    zoom = Math.round(zoom * 100) / 100;
    document.body.removeChild(container);

    return {
      zoom: zoom,
      devicePxPerCssPx: zoom * this.devicePixelRatio,
    };
  }

  /**
   * no real trick; device-pixel-ratio is the ratio of device dpi / css dpi.
   * (Note that this is a different interpretation than Webkit's device
   * pixel ratio, which is the ratio device dpi / system dpi).
   *
   * Also, for Mozilla, there is no difference between the zoom factor and the device ratio.
   */
  private firefox4() {
    const zoom = Math.round(this.mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001) * 100) / 100;
    return {
      zoom: zoom,
      devicePxPerCssPx: zoom,
    };
  }

  /**
   * Firefox 18.x
   * Mozilla added support for devicePixelRatio to Firefox 18,
   * but it is affected by the zoom level, so, like in older
   * Firefox we can't tell if we are in zoom mode or in a device
   * with a different pixel ratio
   */
  private firefox18() {
    return {
      zoom: this.firefox4().zoom,
      devicePxPerCssPx: this.devicePixelRatio,
    };
  }

  /**
   * works starting Opera 11.11
   * the trick: outerWidth is the viewport width including scrollbars in
   * system px, while innerWidth is the viewport width including scrollbars
   * in CSS px
   */
  private opera11() {
    const zoom = Math.round((this.window.top.outerWidth / this.window.top.innerWidth) * 100) / 100;
    return {
      zoom: zoom,
      devicePxPerCssPx: zoom * this.devicePixelRatio,
    };
  }

  binarySearch(property: string, unit: string, a: number, b: number, maxIter: number, epsilon: number, matchMedia: any) {
    const mid = (a + b) / 2;
    if (maxIter <= 0 || b - a < epsilon) {
      return mid;
    }
    const query = '(' + property + ':' + mid + unit + ')';
    if (matchMedia(query).matches) {
      return this.binarySearch(property, unit, mid, b, maxIter - 1, epsilon, matchMedia);
    } else {
      return this.binarySearch(property, unit, a, mid, maxIter - 1, epsilon, matchMedia);
    }
  }

  /**
   * Use a binary search through media queries to find zoom level in Firefox
   */
  private mediaQueryBinarySearch(property: string, unit: string, a: number, b: number, maxIter: number, epsilon: number) {
    let matchMedia;
    let head, style, div;
    if (window.matchMedia) {
      matchMedia = window.matchMedia;
    } else {
      head = document.getElementsByTagName('head')[0];
      style = document.createElement('style');
      head.appendChild(style);

      div = document.createElement('div');
      div.className = 'mediaQueryBinarySearch';
      div.style.display = 'none';
      document.body.appendChild(div);

      matchMedia = function (query) {
        style.sheet.insertRule('@media ' + query + '{.mediaQueryBinarySearch ' + '{text-decoration: underline} }', 0);
        const matched = getComputedStyle(div, null).textDecoration === 'underline';
        style.sheet.deleteRule(0);
        return { matches: matched };
      };
    }
    const ratio = this.binarySearch(property, unit, a, b, maxIter, epsilon, matchMedia);
    if (div) {
      head.removeChild(style);
      document.body.removeChild(div);
    }
    return ratio;
  }

  private detect() {
    //IE8+
    if (!isNaN((this.window.screen as any).logicalXDPI) && !isNaN((this.window.screen as any).systemXDPI)) {
      return this.ie8();
    }
    // IE10+ / Touch
    else if (this.window.navigator.msMaxTouchPoints) {
      return this.ie10();
    }
    //chrome
    else if (!!(this.window as any).chrome && !(!!(this.window as any).opera || this.window.navigator.userAgent.indexOf(' Opera') >= 0)) {
      return this.chrome();
    }
    //safari
    else if (Object.prototype.toString.call((this.window as any).HTMLElement).indexOf('Constructor') > 0) {
      return this.safari();
    }
    //Mobile Webkit
    else if ('orientation' in this.window && 'webkitRequestAnimationFrame' in this.window) {
      return this.webkitMobile();
    }
    //WebKit
    else if ('webkitRequestAnimationFrame' in this.window) {
      return this.webkit();
    }
    //Opera
    else if ((this.window as any).navigator.userAgent.indexOf('Opera') >= 0) {
      return this.opera11();
    }
    //Last one is Firefox
    //FF 18.x
    else if ((this.window as any).devicePixelRatio) {
      return this.firefox18();
    }
    //FF 4.0 - 17.x
    else if (this.firefox4().zoom > 0.001) {
      return this.firefox4();
    }

    return this.fallback();
  }

  zoom() {
    return this.detect().zoom;
  }

  /**
   * Ratios.devicePxPerCssPx shorthand
   */
  device() {
    return this.detect().devicePxPerCssPx;
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant