Detecting Screen Orientation Change

May 2, 2016

Browsers provide different means to detect screen orientation:

Documentation in the Mozilla Developer Network (linked above) states the first to be deprecated but currently still in the WhatWG Living Standard, whereas its documentation on the latter differs from the W3C documentation.

According to documentation, detection of screen orientation change can be achieved by implementing handlers for the events

  • window.orientationchange
  • screen.orientation.change
  • window.matchMedia() listener
  • window.resize

but specific browsers may not support all of these events, with window.resize being the catch-all solution if everything else fails.

So based on SO answers and this blog and this blog I came up with a solution that currently seems to work, and a couple of findings:

  • window.orientation gives the angle on mobile browsers only – desktop browsers always contain 0 (zero).
  • Similarly, window.onorientationchange is only supported by mobile browsers.
  • screen.orientation (and its browser-spezific siblings mozOrientation and msOrientation) contains the angle in its angle property. IE11 does support support screen.orientation on Win7. Mobile Chrome (35) and the Android 4.4.2 Browser do not seem to support it either.
  • Of the browsers I tested, none seem to implement the event screen.orientation.onchange.
  • Orientation change can be detected using the window.matchMedia() listener on both mobile and desktop browsers which support mediaqueries and its orientation selector.
  • In desktop browsers, orientation can only be derived from $(window).width() and $(window).height(), or from the .matches property of a matchMedia listener.

Note that all this need not apply for older browsers, not even the values of window.orientation! (See SO, SO, SO, Giff’s note)

So here now is my JavaScript code for screen orientation change detection:

function doOnOrientationChange(src)
{
  if (window.console && console.log) 
    console.log("width " + $(window).width() + " height " + $(window).height());

  var orientation = { 
    angle: window.orientation,
    type: ("onorientationchange" in window) ? "mobile" : "desktop"  
  };

  if (window.screen) {
    var o = window.screen.orientation || window.screen.mozOrientation 
      || window.screen.msOrientation || orientation;
    orientation = { angle: o.angle, type: o.type };
  } else if ((window.orientation === 0) || window.orientation) {
    orientation = { angle: window.orientation, type: "" + window.orientation + " degrees" };
  }
 
  if (!("onorientationchange" in window)) {
    var w = $(window).width(), h =$(window).height();
    var a = (w > h) ? 90 : 0;
    orientation.angle = a;
    if (window.console && console.log) 
      console.log("angle := " + a + " " + orientation.angle);
  }
 
  var jsonOrientation = JSON.stringify(
    { angle: orientation.angle, type: orientation.type });

  switch(orientation.angle) 
  { 
    case -90:
    case 90:
      // we are in landscape mode
      $().toastmessage('showNoticeToast', src + ' landscape ' + " " + jsonOrientation);
      if (window.console && window.console.log) console.log(src + ' landscape ' + " " + jsonOrientation);
      $("#orientation").text(src + ' landscape ' + " " + jsonOrientation);
      break; 
    case 0:
    case 180:
      // we are in portrait mode
      $().toastmessage('showNoticeToast', src + ' portrait ' + " " + jsonOrientation);
      if (window.console && window.console.log) console.log(src + ' portrait ' + " " + jsonOrientation);
      $("#orientation").text(src + ' portrait ' + " " + jsonOrientation);
      break; 
    default:
      // we have no idea
      $().toastmessage('showNoticeToast', src + ' unknown ' + " " + jsonOrientation);
      if (window.console && window.console.log) console.log(src + ' unknown ' + " " + jsonOrientation);
      $("#orientation").text(src + ' unknown ' + " " + jsonOrientation);
      break; 
  }
}

$(function () {

  if ("onorientationchange" in window) 
    window.addEventListener('orientationchange', 
      function() { doOnOrientationChange("window.orientationchange"); });
  //window.addEventListener('resize', 
  //    function() { doOnOrientationChange("window.resize") });
  if (window.screen && window.screen.orientation && window.screen.orientation.addEventListener)
    window.screen.orientation.addEventListener('change', 
      function() { doOnOrientationChange("screen.orientation.change"); });

  if (window.matchMedia) {
    var mql = window.matchMedia("(orientation: portrait)");
    mql.addListener(function(m) {
      if (m.matches) {
        doOnOrientationChange("mql-portrait");
      } else {
        doOnOrientationChange("mql-landscape");
      }
    });
  }

  doOnOrientationChange("init");
});

(I put the window.resize handler into comments because it generates too may events on desktop browsers.)

In this sample code, detection change only causes output of angle and orientation type to

  • $().toastmessage() – a jQuery extension
  • console.log
  • $(“#orientation”).text() – a jQuery call

Of course, your handlers may perform some useful actions…

Advertisements

Retrieving Chrome tabs suspended by The Great Suspender

January 10, 2016

After a recent Windows crash, restarting Chrome would not open all the windows and tabs I had collected, despite its nice “Restore previous session” question.

None of the tips I found on the net helped recovering those tabs:

Anyway, I found that the tabs suspended by The Great Suspender were most likely not contained in the Tabs file, so where is that information stored?

By looking at the source it turns out that TGS stores its data in an IndexedDB.

To access this database, you need to open TGS’s extension URL chrome-extension://klbibkeccnjlkjkiokjodocebajanakg/suspended.html. Using F12 (Developer Tools) and selecting Resources, you find “tgs” under IndexedDB, containing the tables gsCurrentSessions, gsSuspendedTabInfo, gsPreviews, and gsSavedSessions.

Unfortunately, currently there is no way to export this data directly from the DevTools, so we need a little bit of scripting magic.

After copying and pasting from MDN’s IndexedDB API documentation and sample code and some trial and error, I finally completed a small Javascript script which, when entered in the Developer Tools’ Console, results in a JSON-formatted list of all suspended tabs:

var db; var result=""; var record=0;
var dbor = window.indexedDB.open("tgs");
dbor.onsuccess = function(event) { 
  db = dbor.result;
  db.transaction(["gsSuspendedTabInfo"], "readonly")
    .objectStore("gsSuspendedTabInfo")
    .openCursor()
    .onsuccess= function(event) {
      var c=event.target.result;
      if (c){
        result+=JSON.stringify(c.value)+"\r\n";
        console.log(record++);
        c.continue();
      } else {
        console.log(result);
      }
    };
};

A single JSON record of a suspended tab looks like this:

{
  "date":"2015-12-14T18:40:55.563Z",
  "title":".validate() | jQuery Validation Plugin",
  "url":"http://jqueryvalidation.org/validate#toptions",
  "favicon":"chrome://favicon/http://jqueryvalidation.org/validate#toptions",
  "pinned":false,
  "index":3,
  "windowId":2898,
  "id":1281
}

Of course, you can process each tab’s record (c.value in the code above) any way you need.


Browser Screenshot Extensions

September 2, 2014

If you want to take a screenshot of your current browser window, there’s always good old ALT-Printscreen, but this function captures the whole window, not just the contents, and copies it to the clipboard. Then you still need to open a graphics editor, such as Paint.Net, to crop, edit, and save the image.

There are, however, a couple of browser extensions to simplify the process, and support capturing the complete page contents, rather than just the visible part of the page.

Here’s the list of extensions I use:

Firefox

In Firefox, I use Screengrab (fix version). It allows you to save or copy-to-clipboard the complete page, the visible part, or a selected area of the current page.

In the settings, you can define the pattern of the file name of the saved image (default: HTML Title and timestamp), and the text that is generated at the top of the image (default: URL). The option “Quickly save” won’t prompt you for a file name.

I love this extension for Firefox – however, if the screenshot gets too big (about 1.5Mb on Win32, 3Mb on Win32), it silently fails and generates .png files of size 0).

Chrome

The extension Screen Capture (by Google) is now unsupported, and it did not work (read: the menu buttons did not invoke any recognizable action) on the latest versions of Chrome.

The extension Awesome Screenshot: Capture & Annotate supports capturing the complete page, the visible and a selected part of the page. After capturing, a simply picture editor allows you to crop the picture, or add simple graphics and text to the image. The file name of the saved image defaults to the page’s Title, but can be edited in the Save As dialog.

Unfortunately, only the command “Capture visible part of page” works on Facebook pages – both “entire page” and “selected area” fail to capture.

Finally, the extension Full Page Screen Capture simply generates an image of the complete page, and displays it in a new tab. From there, you need to invoke Save (ctrl-S) to save the image to the default directory. File name pattern is “screencapture-” plus the current URL. This extension provides no options.


Browser Innovation

December 30, 2012

Compare:

All images from the ArsTechnica article Windows 8 takes 1 percent of Web usage as Internet Explorer gains using data from NetMarketShare.


Chrome, Firefox reset Flash plugin if display: property changes (Update)

May 11, 2012

To resolve the mystery of Flash plugins restarting when their display property changes (directly or inherited), I created a couple of plain and simple HTML files to test a set of operations to toggle visibity in IE 9 (9.0.8112.16421), Chrome 18 (18.0.1025.168 m), and Firefox 12 (12.0).

The Flash player has version 11.1.102.55, and the Flash object is initialized by swfobject.js.

Each of the 3 browsers were tested on 2 different pages with 3 different toggle operations:

  • Page 1 contained the Flash object inside an iframe inside a div.

DOM hierarchy:
<div><iframe><html etc><div class=”flashContent”>

  • Page 2 contains the Flash object inside a div.

DOM hierarchy:
<div><div class=”flashContent”>

The toggle operations were

  • assign a .hidden class setting visibility to hidden
.hidden { visibility: hidden; width: 0px; height: 0px; }
  • assign a .displaynone class setting display to none
.displaynone { display: none; }
  • using jquery .show() and .hide()

.show() actually does not clear the display: property, but sets it to ‘block’, ‘inline’ etc, which might interfere with other operations, such as addClass(“displaynone”), etc.

Results for Page 1 (using iframe):

IE 9 Chrome 18 Firefox 12
toggling top-level div
hidden ok ok ok
display:none ok ok restart
show/hide ok ok restart
toggling iframe
hidden ok ok ok
display:none ok ok restart
show/hide ok ok restart

Results for Page 2 (directly embedded Flash):

IE 9 Chrome 18 Firefox 12
toggling top-level div
hidden ok ok ok
display:none ok restart restart
show/hide ok restart restart
toggling flash object
hidden ok ok ok
display:none ok restart restart
show/hide ok restart restart

The only method to set a Flash object hidden and visible again while keeping the object running turns out to be setting a CSS class with visibility:hidden.

So I put the code (swf inside iframe) that worked in plain HTML files and added it into a DotNetNuke installation. It almost worked.

A new issue occurred: IE9 was not able to display a Flash object once the visibility:hidden style was removed! F12 did not help me to make the object visible again. Strangely though, using JavaScript to re-assign some other iframe to the same src URL caused the Flash to display again!

$("#someotheriframe").attr("src", 
    $("#someotheriframe").attr("src"));

A DNN page simply contains too much generated code (ASP.Net, MS Ajax, DNN framework, jQuery, various controls and .js files) so that I did not think it was worth debugging the issue further. My JavaScript functions to toggle visibility now contain a browser detection based on $.browser to decide which alternative to choose.


Chrome, Firefox reset Flash plugin if display: property changes

May 7, 2012

Working on a web site that dynamically (i.e. upon user action) shows and hides HTML content and Flash content, everything worked as expected in IE9.

When I checked the same page in Chrome 18 and Firefox 12, however, the Flash object would reset everytime I invoked the jQuery .show() method on the object (initialization takes some time for some of the objects I embed).

This behavior has already been mentioned in blogs and support forums.

My solution was to roll-back some of the changes I made, and put the Flash object back to its iframe. Toggling the iframe display does not seem to affect Flash.

Other people worked around this behavior by setting visibility and size, or adding/removing a class that defines display:none.