Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mobile-viewport-control

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mobile-viewport-control - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

bookmarklet/build.sh

8

CHANGES.md
# Changes
## 0.3.0
- iOS - zoom bounds restored on thaw
- UIWebView - partial freeze support (double-tap or pinch-zoom auto-zooms)
- Android Firefox - scroll fixed on thaw
- Android Firefox - page width fixed on freeze
- Android Chrome/WebView - scroll/scale fixed on thaw
## 0.2.0

@@ -4,0 +12,0 @@

209

index.js
//
// Mobile Viewport Control v0.2.0
// Mobile Viewport Control v0.3.0
//

@@ -32,4 +32,4 @@ // Copyright (c) 2016 Shaun Williams

return {
left: document.body.scrollLeft,
top: document.body.scrollTop,
top: window.pageYOffset || document.documentElement.scrollTop,
left: window.pageXOffset || document.documentElement.scrollLeft
};

@@ -39,4 +39,3 @@ }

function setScroll(scroll) {
document.body.scrollLeft = scroll.left;
document.body.scrollTop = scroll.top;
window.scrollTo(scroll.left, scroll.top);
}

@@ -49,9 +48,14 @@

function getInitialViewport() {
// These seem to be the defaults
var viewport = {
'user-scalable': 'yes',
'minimum-scale': '0',
'maximum-scale': '10'
};
function getInitialViewport(withDefaults) {
var viewport = {};
if (withDefaults) {
// These seem to be the defaults
viewport = {
'user-scalable': 'yes',
'minimum-scale': '0',
'maximum-scale': '10'
};
}
var tags = document.querySelectorAll('meta[name=viewport]');

@@ -75,2 +79,21 @@ var i,j,tag,content,keyvals,keyval;

function getPrettyInitialViewport() {
var initial = getInitialViewport();
var keyvals = [];
for (var prop in initial) {
if (initial.hasOwnProperty(prop)) {
keyvals.push({key:prop, val:initial[prop]});
}
}
return (
keyvals.sort(function(a,b) {
if (a.key < b.key) return -1;
if (a.key > b.key) return 1;
return 0;
}).map(function(kv) {
return kv.key + '=' + kv.val;
}).join(',\n')
);
}
//---------------------------------------------------------------------------

@@ -104,3 +127,26 @@ // Calculating current viewport scale

//---------------------------------------------------------------------------
// Get mobile OS
// from: http://stackoverflow.com/a/21742107
//---------------------------------------------------------------------------
function getMobileOS() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
if (userAgent.match(/iPad/i) ||
userAgent.match(/iPhone/i) ||
userAgent.match(/iPod/i)) {
return 'iOS';
}
else if (userAgent.match(/Android/i)) {
return 'Android';
}
}
function isFirefox() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
return userAgent.match(/Firefox/i) ? true : false;
}
//---------------------------------------------------------------------------
// State and Constants

@@ -201,3 +247,3 @@ //---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Freezing and Thawing
// Freezing
//---------------------------------------------------------------------------

@@ -220,12 +266,21 @@

// save original viewport state
originalScroll = getScroll();
originalScale = getScale();
// isolate element if needed
if (isolateID) {
isolate(isolateID);
setScroll({x:0,y:0});
}
// freeze viewport
// validate scale
// (we cannot freeze scale at 1.0 on Android)
if (scale === 1) {
scale = 1.002;
}
// add our new meta viewport tag
var hook = document.getElementById(hookID);
if (!hook) {
originalScale = getScale();
originalScroll = getScroll();
hook = document.createElement('meta');

@@ -236,8 +291,19 @@ hook.id = hookID;

}
// When freezing the viewport, we still enable
// user-scalability and allow a tight zooming
// margin. Without this, UIWebView would simply
// ignore attempts to set the scale. But with this
// solution, the next time the user pinch-zooms
// in this state, the viewport will auto-snap
// to our scale.
var includeWidth = (getMobileOS() === 'Android' && isFirefox());
hook.setAttribute('content', [
'user-scalable=no',
'user-scalable=yes',
'initial-scale='+scale,
'minimum-scale='+scale,
'maximum-scale='+scale
].join(','));
'maximum-scale='+(scale+0.004),
(includeWidth ? 'width=device-width' : null)
].filter(Boolean).join(','));

@@ -249,19 +315,8 @@ if (onDone) {

// Thaw the viewport, restoring the scale and scroll to what it
// was before freezing.
function thaw(onDone, testEvts) {
// restore body visibility
var style = document.getElementById(styleID);
if (style) {
undoIsolate();
}
//---------------------------------------------------------------------------
// Thawing
//---------------------------------------------------------------------------
// exit if there is nothing to thaw
var hook = document.getElementById(hookID);
if (!hook) {
return;
}
function thawIOS(hook, initial, onDone) {
var initial = getInitialViewport();
// Restore the user's manual zoom.

@@ -271,37 +326,70 @@ hook.setAttribute('content', [

'minimum-scale='+originalScale,
'maximum-scale='+originalScale,
'maximum-scale='+originalScale
].join(','));
// Restore the page's zoom bounds.
hook.setAttribute('content', [
'user-scalable='+initial['user-scalable'],
'minimum-scale='+initial['minimum-scale'],
'maximum-scale='+initial['maximum-scale'],
(initial.width ? 'width='+initial.width : null)
].filter(Boolean).join(','));
// Remove our meta viewport hook.
document.head.removeChild(hook);
setScroll(originalScroll);
setTimeout(function() {
if (testEvts && testEvts.onRestoreScale)
testEvts.onRestoreScale();
if (onDone)
onDone();
}, refreshDelay);
}
// Restore the page's zoom bounds.
hook.setAttribute('content', [
(initial.width ? ('width=' + initial.width) : null),
'user-scalable='+initial['user-scalable'],
'initial-scale='+originalScale,
'minimum-scale='+initial['minimum-scale'],
'maximum-scale='+initial['maximum-scale']
].filter(Boolean).join(','));
function thawAndroid(hook, initial, onDone) {
hook.setAttribute('content', [
'user-scalable='+initial['user-scalable'],
'initial-scale='+originalScale,
'minimum-scale='+initial['minimum-scale'],
'maximum-scale='+initial['maximum-scale'],
(initial.width ? 'width='+initial.width : null)
].filter(Boolean).join(','));
setTimeout(function(){
if (testEvts && testEvts.onRestoreBounds)
testEvts.onRestoreBounds();
setScroll(originalScroll);
// Remove our meta viewport hook.
document.head.removeChild(hook);
setTimeout(function(){
document.head.removeChild(hook);
if (onDone)
onDone();
}, refreshDelay);
}
// Updating the viewport can change scroll,
// so we have to do this last.
setScroll(originalScroll);
// Thaw the viewport, restoring the scale and scroll to what it
// was before freezing.
function thaw(onDone) {
// restore body visibility
var style = document.getElementById(styleID);
if (style) {
undoIsolate();
}
if (testEvts && testEvts.onRestoreScroll)
testEvts.onRestoreScroll();
// exit if there is nothing to thaw
var hook = document.getElementById(hookID);
if (!hook) {
return;
}
if (onDone)
onDone();
var initial = getInitialViewport(true);
}, refreshDelay);
}, refreshDelay);
var os = getMobileOS();
if (os === 'iOS') {
thawIOS(hook, initial, onDone);
}
else if (os === 'Android') {
thawAndroid(hook, initial, onDone);
}
else {
// For other browsers, we just try Android's method.
thawAndroid(hook, initial, onDone);
}
}

@@ -315,2 +403,3 @@

getInitialViewport: getInitialViewport,
getPrettyInitialViewport: getPrettyInitialViewport,
getScale: getScale,

@@ -321,3 +410,3 @@ isolate: isolate,

// stable
version: '0.2.0',
version: '0.3.0',
freeze: freeze,

@@ -324,0 +413,0 @@ thaw: thaw

{
"name": "mobile-viewport-control",
"version": "0.2.0",
"version": "0.3.0",
"description": "Dynamically control the mobile viewport",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -50,14 +50,7 @@ # Mobile Viewport Control

## Try it on Existing Web Pages
## Compatibility Testing
Test on arbitrary web pages with the following bookmarklet. It will freeze
the viewport scale, show a custom isolated element, and allow you to press
a button to restore the view.
> __NOTE__: We do not test with Simulators, as iOS Simulator has been shown to
> produce different results than the real environment.
```
javascript:(function(){document.body.appendChild(document.createElement('script')).src='https://cdn.rawgit.com/shaunstripe/mobile-viewport-control/master/index.js?'+Math.random();document.body.appendChild(document.createElement('script')).src='https://cdn.rawgit.com/shaunstripe/mobile-viewport-control/master/test/bookmarklet.js?'+Math.random();}())
```
## Compatibility
Compatibility is measured with a combination of automatic/manual testing:

@@ -72,18 +65,19 @@

1. Manual step - verify that you can still pinch-zoom after the test
1. __[Injection Method]__ - verify it works for any webpage by running a bookmarklet test to inject JS into the page
| Mobile Browser | [Measure Test]\* | [Freeze Test] | [Thaw Test] |
|----------------------------|------------------|-----------------------------------------------|-----------------------|
| iOS Safari | Y | Y | Y |
| iOS UIWebView | Y | Fails if user pinch-zooms before freezing\*\* | Y if freeze succeeds. |
| iOS WKWebView | Y | Y | Y |
| iOS SFSafariViewController | Y | Y | Y |
| iOS Chrome | Y | Y | Y |
| iOS Firefox | Y | Y | Y |
| iOS Opera Mini | Y | Fails if user pinch-zooms before freezing\*\* | Y if freeze succeeds. |
| Android Browser (Stock) | ? | ? | ? |
| Android Chrome | Y | Y | Y |
| Android WebView | Y | Y | Y |
| Android Chrome Custom Tabs | Y | Y | Y |
| Android Firefox | Y | Fails | Fails |
| Android Opera Mini | Fails | Fails | Fails |
| Mobile Browser | [Measure Test]\* | [Freeze Test] | [Thaw Test] | [Injection Method] |
|----------------------------|------------------|---------------|--------------------|--------------------------|
| iOS Safari | Y | Y | Y | devtools |
| iOS UIWebView | Y | Fails\*\* | Y if freeze works. | xcode+devtools |
| iOS WKWebView | Y | Y | Y | xcode+devtools |
| iOS SFSafariViewController | Y | Y | Y | |
| iOS Chrome | Y | Y | Y | bookmarklet |
| iOS Firefox | Y | Y | Y | |
| iOS Opera Mini | Y | Fails\*\* | Y if freeze works. | |
| Android Browser (Stock) | ? | ? | ? | |
| Android Chrome | Y | Y | Y | devtools or bookmarklet |
| Android WebView | Y | Y | Y | devtools |
| Android Chrome Custom Tabs | Y | Y | Y | devtools |
| Android Firefox | Y | Fails | Fails | devtools or bookmarklet |
| Android Opera Mini | Fails | Fails | Fails | |

@@ -93,5 +87,6 @@ _\* This test fails in the iOS Simulator because `initial-scale` is ignored

_\*\* Pinch-zooming causes the page's scale to change from its specified
`initial-scale`. This custom zoom level is maintained across refreshes. When
opening in a new tab, the `initial-scale` is resumed._
_\*\* Fails if user pinch-zooms before freezing. Pinch-zooming causes the
page's scale to change from its specified `initial-scale`. This custom zoom
level is maintained across refreshes. When opening in a new tab, the
`initial-scale` is resumed._

@@ -101,3 +96,40 @@ [Measure Test]:http://shaunstripe.github.io/mobile-viewport-control/test/01-measure.html

[Thaw Test]:http://shaunstripe.github.io/mobile-viewport-control/test/03-thaw.html
[Injection Method]:#injecting-into-existing-pages
### Injecting into Existing Pages
To allow us to quickly gauge compatibility for externally owned webpages
across several browsers, we must be able to inject our library code into
their running pages.
##### With Bookmarklet
Bookmarklets are URLs of the form `javascript:(function(){ ... }())` which some
browsers allow for evaluating JS in the context of the current page. The
mobile browsers compatible are listed in the above table under the Injection
Method column.
The following bookmarklet will freeze the viewport scale, show a custom
isolated element, and allow you to press a button to restore the view.
```js
javascript:(function(){document.body.appendChild(document.createElement('script')).src='https://rawgit.com/shaunstripe/mobile-viewport-control/master/bookmarklet/index.js?'+Math.random();}())
```
##### With DevTools
Some mobile browsers allow you to connect to a Desktop DevTools environment
with a JS console for evaluating JS in the context of its page. iOS Safari
supports connecting to Desktop Safari DevTools in this way. Android
Chrome/webview can connect to Desktop Chrome DevTools. Inside DevTools, we can
simply paste the body of the bookmarklet inside the JS console:
```js
document.body.appendChild(document.createElement('script')).src='https://rawgit.com/shaunstripe/mobile-viewport-control/master/bookmarklet/index.js?'+Math.random();
```
iOS webviews require an extra step: you must run an webview app from a live
XCode project on your mac. Only then will Desktop Safari DevTools to connect
to its webview.
### Variables

@@ -117,4 +149,11 @@

### Quirks when Dynamically Modifying Viewport
- iOS UIWebView does not allow scale adjustments after the user has manually adjusted it.
- Android does not register a new viewport meta tag if it is immediately removed after creation.
- Android will modify the scroll some time after a new viewport tag is registered.
- Android (at least in Chrome) will not freeze scale at 1.0, but will at 1.0±ε for some ε>0.
## License
[ISC License](LICENSE)

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc