Skip to content

Instantly share code, notes, and snippets.

@mmocny
Last active February 2, 2021 17:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmocny/012250227249f53d7758da7b18e876d5 to your computer and use it in GitHub Desktop.
Save mmocny/012250227249f53d7758da7b18e876d5 to your computer and use it in GitHub Desktop.
Re-Implement Chrome conventions for mobile viewport test in JS.
/*
* Re-implement based on conventions from:
* https://source.chromium.org/chromium/chromium/src/+/master:cc/trees/layer_tree_host_impl.cc;l=160-176
*
* With help from github.com/dontcallmedom and his library:
* https://github.com/dontcallmedom/metaviewport-parser (MIT https://github.com/dontcallmedom/metaviewport-parser/blob/master/LICENSE)
*/
function HasFixedPageScale() {
// Simpler variant:
// document.head.querySelector('meta[name=viewport]').getAttribute('content').split(/ *, */).forEach(v => { val = v.split(/ *= */, 2); attrs[val[0]] = val[1] })
// From: https://github.com/dontcallmedom/metaviewport-parser
function parseMetaViewPortContent(S) {
var parsedContent = {
validProperties : {},
unknownProperties: {},
invalidValues : {}
};
var i = 1;
while (i <= S.length) {
while (i <= S.length && RegExp(' |\x0A|\x09|\0d|,|;|=').test(S[i-1])) {
i++;
}
if (i <= S.length) {
i = parseProperty(parsedContent, S, i);
}
}
return parsedContent;
};
function parseProperty(parsedContent, S, i) {
var start = i;
while (i <= S.length && !RegExp(' |\x0A|\x09|\0d|,|;|=').test(S[i-1])) {
i++;
}
if (i > S.length || RegExp(',|;').test(S[i-1])) {
return i;
}
var propertyName = S.slice(start - 1, i-1);
while (i <= S.length && !RegExp(',|;|=').test(S[i-1])) {
i++;
}
if (i > S.length || RegExp(',|;').test(S[i-1])) {
return i;
}
while (i <= S.length && RegExp(' |\x0A|\x09|\0d|=').test(S[i-1])) {
i++;
}
if (i > S.length || RegExp(',|;').test(S[i-1])) {
return i;
}
start = i;
while (i <= S.length && !RegExp(' |\x0A|\x09|\0d|,|;|=').test(S[i-1])) {
i++;
}
var propertyValue = S.slice(start - 1, i-1);
setProperty(parsedContent, propertyName, propertyValue);
return i;
}
function setProperty(parsedContent, name, value) {
if (propertyNames.indexOf(name) >= 0) {
var number = parseFloat(value);
if (!isNaN(number)) {
parsedContent.validProperties[name] = number;
return;
}
var string = value.toLowerCase();
if (string === "yes" || string === "no" || string === "device-width" || string === "device-height" ||
// https://webkit.org/blog/7929/designing-websites-for-iphone-x/
(name.toLowerCase() === 'viewport-fit' && (string === 'auto' || string === 'cover'))) {
parsedContent.validProperties[name] = string;
return;
}
parsedContent.validProperties[name] = null;
parsedContent.invalidValues[name] = value;
} else {
parsedContent.unknownProperties[name] = value;
}
}
var propertyNames = ["width", "height", "initial-scale", "minimum-scale", "maximum-scale", "user-scalable", "shrink-to-fit", "viewport-fit"];
//let sample = "width=device-width, initial-scale=1";
let vp = document.head.querySelector('meta[name=viewport]');
if (!vp) return false;
let vpc = parseMetaViewPortContent(vp.getAttribute("content") || "");
if (!vpc) return false;
let { "minimum-scale": minScale, "maximum-scale": maxScale } = vpc.validProperties;
return Number.isFinite(minScale) && Number.isFinite(maxScale) && minScale == maxScale;
}
function HasMobileViewport() {
return document.scrollingElement.scrollWidth <= document.scrollingElement.clientWidth + 0.15;
}
function IsMobileOptimized() {
return HasFixedPageScale() || HasMobileViewport();
}
// I'm not sure if there are more signals than just these two.
function HasTapDelay() {
let vp = document.head.querySelector('meta[name=viewport]');
return !vp || !IsMobileOptimized();
}
console.log("HasTapDelay:" , HasTapDelay());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment