diff --git a/api/cont.js b/api/cont.js index 2c18611c..e7100b53 100644 --- a/api/cont.js +++ b/api/cont.js @@ -226,6 +226,11 @@ return Async(this, function(cont, async) { if (args.length == 1) data = cont.all(); dust.render(template, data, function(err, out) { + // Apply scroll fix to iOS 8.0 + if (out && Mobify.isIOS8_0()) { + out = Mobify.ios8_0ScrollFix(out); + } + if (err) { async.finish(out); debug.die(err); diff --git a/api/util.js b/api/util.js index d6ecb8a1..31295717 100644 --- a/api/util.js +++ b/api/util.js @@ -81,6 +81,68 @@ return i18nlookup; }; + Mobify.isIOS8_0 = function() { + var IOS8_REGEX = /ip(hone|od|ad).*OS 8_0/i; + + return IOS8_REGEX.test(window.navigator.userAgent); + }; + + /** + * iOS 8.0 has a bug where dynamically switching the viewport (by swapping the + * viewport meta tag) causes the viewport to automatically scroll. When + * capturing, the initial document never has an active meta viewport tag. + * Then, the rendered document injects one causing the aforementioned scroll. + * + * This patches HTML to hide the body until the first paint (and hopefully after + * the initial viewport is calculated). By the time we show the body the new + * viewport should have already taken effect. + * + * JIRA: https://mobify.atlassian.net/browse/GOLD-883 + * Open Radar: http://www.openradar.me/radar?id=5516452639539200 + * WebKit Bugzilla: https://bugs.webkit.org/show_bug.cgi?id=136904 + */ + Mobify.ios8_0ScrollFix = function(htmlString) { + var BODY_REGEX = /'"]*|'[^']*?'|"[^"]*?")*>/i; + + var openingBodyTag = BODY_REGEX.exec(htmlString); + // Do nothing if we can't find an opening `body` tag. + if (!openingBodyTag) { + return htmlString; + } + openingBodyTag = openingBodyTag[0]; + + // Use DOM methods to manipulate the attributes on the `body` tag. This + // lets us rely on the browser to set body's style to `display: none`. + // We create a containing element to be able to set an inner HTML string. + var divEl = document.createElement('div'); + + // The `div`'s inner string can't be a `body` tag, so we temporarily change + // it to a `div`.. + var openingBodyTagAsDiv = openingBodyTag.replace(/^$/, ''); + openingBodyTag = openingBodyTagAsDiv.replace(/^
" + + " window.requestAnimationFrame(function() {" + + " window.requestAnimationFrame(function() {" + + " document.body.style.display = '';" + + " });" + + " });" + + "<\/script>"; + + return htmlString.replace(BODY_REGEX, openingBodyTag + script); + }; + // ### // # SERVICES // ### diff --git a/test/util-test.html b/test/util-test.html index eaecb00c..dcbce87b 100644 --- a/test/util-test.html +++ b/test/util-test.html @@ -232,6 +232,31 @@

equal(Mobify.getImageURL('http://test/1.jpg', {maxWidth: 320}), '//ir3.mobify.com/320/http://test/1.jpg') }); +test("Mobify.ios8_0ScrollFix", function() { + var html = + "" + + "" + + " Scroll Fix Test" + + "" + + "" + + " " + + "" + + ""; + + html = Mobify.ios8_0ScrollFix(html); + + ok(html.indexOf('font-size: 13px') !== -1, + '`font-size: 13px` on the body tag is still present'); + ok(html.indexOf('display: none') !== -1, + '`display: none` was set on the body tag'); + ok(html.indexOf("document.body.style.display = '';") !== -1, + 'there is a script to show the body'); + ok(html.indexOf('x-test') !== -1, + '`x-test` body class tag is still present'); +}); + test('$.fn.resizeImages', function() { var $resized = Mobify.$('#test-resizeImages').resizeImages() equal($resized.length, 3)