aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorPiotr Wilkin <piotr.wilkin@syndatis.com>2017-09-25 10:25:28 +0200
committerHenri Sara <henri.sara@gmail.com>2017-09-25 11:25:28 +0300
commitbcf58e21450831c4956366222ee93af9e65d0034 (patch)
treed4e092bf6707468ee4fd380363b9c6e69325f698 /server
parent7195e13f0de43dc9f3927b00c85b7740e31880fe (diff)
downloadvaadin-framework-bcf58e21450831c4956366222ee93af9e65d0034.tar.gz
vaadin-framework-bcf58e21450831c4956366222ee93af9e65d0034.zip
Pass timezone ID from browser to server. (#10041)
Resolves #7911. Prerequisite for #10033.
Diffstat (limited to 'server')
-rw-r--r--server/src/main/java/com/vaadin/server/WebBrowser.java20
-rw-r--r--server/src/main/resources/VAADIN/vaadinBootstrap.js734
2 files changed, 390 insertions, 364 deletions
diff --git a/server/src/main/java/com/vaadin/server/WebBrowser.java b/server/src/main/java/com/vaadin/server/WebBrowser.java
index fd32a42708..f9304e5281 100644
--- a/server/src/main/java/com/vaadin/server/WebBrowser.java
+++ b/server/src/main/java/com/vaadin/server/WebBrowser.java
@@ -42,6 +42,7 @@ public class WebBrowser implements Serializable {
private int rawTimezoneOffset = 0;
private int dstSavings;
private boolean dstInEffect;
+ private String timeZoneId;
private boolean touchDevice;
private VBrowserDetails browserDetails;
@@ -354,6 +355,17 @@ public class WebBrowser implements Serializable {
}
/**
+ * Returns the TimeZone Id (like "Europe/Helsinki") provided by the browser
+ * (if the browser supports this feature).
+ *
+ * @return the TimeZone Id if provided by the browser, null otherwise.
+ * @see <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/resolvedOptions">Intl.DateTimeFormat.prototype.resolvedOptions()</a>
+ */
+ public String getTimeZoneId() {
+ return timeZoneId;
+ }
+
+ /**
* Returns the browser-reported TimeZone offset in milliseconds from GMT
* ignoring possible daylight saving adjustments that may be in effect in
* the browser.
@@ -450,7 +462,7 @@ public class WebBrowser implements Serializable {
* @param touchDevice
*/
void updateClientSideDetails(String sw, String sh, String tzo, String rtzo,
- String dstSavings, String dstInEffect, String curDate,
+ String dstSavings, String dstInEffect, String tzId, String curDate,
boolean touchDevice) {
if (sw != null) {
try {
@@ -487,6 +499,11 @@ public class WebBrowser implements Serializable {
if (dstInEffect != null) {
this.dstInEffect = Boolean.parseBoolean(dstInEffect);
}
+ if (tzId == null || "undefined".equals(tzId)) {
+ this.timeZoneId = null;
+ } else {
+ this.timeZoneId = tzId;
+ }
if (curDate != null) {
try {
long curTime = Long.parseLong(curDate);
@@ -525,6 +542,7 @@ public class WebBrowser implements Serializable {
request.getParameter("v-rtzo"),
request.getParameter("v-dstd"),
request.getParameter("v-dston"),
+ request.getParameter("v-tzid"),
request.getParameter("v-curdate"),
request.getParameter("v-td") != null);
}
diff --git a/server/src/main/resources/VAADIN/vaadinBootstrap.js b/server/src/main/resources/VAADIN/vaadinBootstrap.js
index a33884eee3..7cf133ac56 100644
--- a/server/src/main/resources/VAADIN/vaadinBootstrap.js
+++ b/server/src/main/resources/VAADIN/vaadinBootstrap.js
@@ -1,369 +1,377 @@
-(function() {
- var apps = {};
- var themesLoaded = {};
- var widgetsets = {};
-
-
+(function () {
+ var apps = {};
+ var themesLoaded = {};
+ var widgetsets = {};
+
+
var log;
if (typeof console === "undefined" || !window.location.search.match(/[&?]debug(&|$)/)) {
- //If no console.log present, just use a no-op
- log = function() {};
+ //If no console.log present, just use a no-op
+ log = function () {
+ };
} else if (typeof console.log === "function") {
- //If it's a function, use it with apply
- log = function() {
- console.log.apply(console, arguments);
- };
+ //If it's a function, use it with apply
+ log = function () {
+ console.log.apply(console, arguments);
+ };
} else {
- //In IE, its a native function for which apply is not defined, but it works without a proper 'this' reference
- log = console.log;
+ //In IE, its a native function for which apply is not defined, but it works without a proper 'this' reference
+ log = console.log;
}
-
- var loadTheme = function(url, version) {
- if(!themesLoaded[url]) {
- log("loadTheme", url, version);
-
- var href = url + '/styles.css';
- if (version) {
- href += '?v=' + version;
- }
-
- var stylesheet = document.createElement('link');
- stylesheet.setAttribute('rel', 'stylesheet');
- stylesheet.setAttribute('type', 'text/css');
- stylesheet.setAttribute('href', href);
- document.getElementsByTagName('head')[0].appendChild(stylesheet);
- themesLoaded[url] = true;
- }
- };
-
- var isWidgetsetLoaded = function(widgetset) {
- var className = widgetset.replace(/\./g, "_");
- return (typeof window[className]) != "undefined";
- };
-
- var loadWidgetset = function(url, widgetset, ready) {
- if (widgetsets[widgetset]) {
- return;
- }
- log("load widgetset", url, widgetset);
- setTimeout(function() {
- if (!isWidgetsetLoaded(widgetset)) {
- if (ready) {
- alert("Failed to load the widgetset: " + url);
- } else {
- if (window.confirm("Failed to load the widgetset. If using CDN for the widgetset, it is possible that compiling it takes up to a few minutes. Would you like to try again?")) {
- window[widgetset] = undefined;
- window.location.reload(false);
- } else {
- alert("Failed to load the widgetset: " + url);
- }
- }
- }
- }, 15000);
-
- var scriptTag = document.createElement('script');
- scriptTag.setAttribute('type', 'text/javascript');
- scriptTag.setAttribute('src', url);
- document.getElementsByTagName('head')[0].appendChild(scriptTag);
-
- widgetsets[widgetset] = {
- pendingApps: []
- };
- };
-
- var isInitializedInDom = function(appId) {
- var appDiv = document.getElementById(appId);
- if (!appDiv) {
- return false;
- }
- for ( var i = 0; i < appDiv.childElementCount; i++) {
- var className = appDiv.childNodes[i].className;
- // If the app div contains a child with the class
- // "v-app-loading" we have only received the HTML
- // but not yet started the widget set
- // (UIConnector removes the v-app-loading div).
- if (className && className.indexOf("v-app-loading") != -1) {
- return false;
- }
- }
- return true;
- };
-
- window.vaadin = window.vaadin || {
- initApplication: function(appId, config) {
- var testbenchId = appId.replace(/-\d+$/, '');
-
- if (apps[appId]) {
- if (window.vaadin && window.vaadin.clients && window.vaadin.clients[testbenchId] && window.vaadin.clients[testbenchId].initializing) {
- throw "Application " + appId + " is already being initialized";
- }
- if (isInitializedInDom(appId)) {
- throw "Application " + appId + " already initialized";
- }
- }
-
- log("init application", appId, config);
-
- window.vaadin.clients[testbenchId] = {
- isActive: function() {
- return true;
- },
- initializing: true
- };
-
- var getConfig = function(name) {
- var value = config[name];
- return value;
- };
-
- var fetchRootConfig = function(callback) {
- log('Fetching root config');
- var url = getConfig('browserDetailsUrl');
- if (!url) {
- // No special url defined, use the same URL that loaded this page (without the fragment)
- url = window.location.href.replace(/#.*/,'');
- }
- // Timestamp to avoid caching
- url += ((/\?/).test(url) ? "&" : "?") + "v-" + (new Date()).getTime();
-
- var params = "v-browserDetails=1";
- var rootId = getConfig("v-rootId");
- if (rootId !== undefined) {
- params += "&v-rootId=" + rootId;
- }
-
- // Tell the UI what theme it is configured to use
- var theme = getConfig('theme');
- if (theme !== undefined) {
- params += '&theme=' + encodeURIComponent(theme);
- }
-
- params += "&v-appId=" + appId;
-
- var extraParams = getConfig('extraParams')
- if (extraParams !== undefined) {
- params += extraParams;
- }
-
- params += '&' + vaadin.getBrowserDetailsParameters(appId, getConfig('sendUrlsAsParameters'));
-
- var r;
- try {
- r = new XMLHttpRequest();
- } catch (e) {
- r = new ActiveXObject("MSXML2.XMLHTTP.3.0");
- }
- r.open('POST', url, true);
- r.onreadystatechange = function (aEvt) {
- if (r.readyState == 4) {
- // Save responseStatus so as Offline Applications know what happened
- // when loading root configuration from server, and depending on the
- // error status display an error message or the offline UI.
- config.rootResponseStatus = r.status;
- config.rootResponseText = r.responseText;
-
- var text = r.responseText;
- if (r.status == 200){
- log("Got root config response", text);
- var updatedConfig = JSON.parse(text);
-
- // Copy new properties to the config object
- for (var property in updatedConfig) {
- if (updatedConfig.hasOwnProperty(property)) {
- config[property] = updatedConfig[property];
- }
- }
-
- // Try bootstrapping again, this time without fetching missing info
- bootstrapApp(false);
- } else {
- log('Error', r.statusText, text);
-
- //Let TB waitForVaadin work again
- delete window.vaadin.clients[testbenchId];
-
- // Show the error in the app's div
- var appDiv = document.getElementById(appId);
- appDiv.innerHTML = text;
- appDiv.style['overflow'] = 'auto';
- }
-
- // Run the fetchRootConfig callback if present.
- callback && callback(r);
- }
- };
- // send parameters as POST data
- r.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
- r.send(params);
-
- log('sending request to ', url);
- };
-
- //Export public data
- var app = {
- getConfig: getConfig,
- // Used when the app was started in offline, so as it is possible
- // to defer root configuration loading until network is available.
- fetchRootConfig: fetchRootConfig
- };
- apps[appId] = app;
-
- if (!window.name) {
- window.name = appId + '-' + Math.random();
- }
-
- var bootstrapApp = function(mayDefer) {
- var vaadinDir = getConfig('vaadinDir');
-
- var versionInfo = getConfig('versionInfo');
-
- var themeUri = vaadinDir + 'themes/' + getConfig('theme');
- loadTheme(themeUri, versionInfo && versionInfo['vaadinVersion']);
-
- var widgetset = getConfig('widgetset');
- var widgetsetUrl = getConfig('widgetsetUrl');
- if (!widgetsetUrl) {
- widgetsetUrl = vaadinDir + 'widgetsets/' + widgetset + "/" + widgetset + ".nocache.js?" + new Date().getTime();
- }
- var widgetsetReady = getConfig('widgetsetReady');
- loadWidgetset(widgetsetUrl, widgetset, widgetsetReady);
-
- if (getConfig('uidl') === undefined) {
- if (mayDefer) {
- fetchRootConfig();
- } else {
- throw "May not defer bootstrap any more";
- }
- } else {
- if (widgetsets[widgetset].callback) {
- log("Starting from bootstrap", appId);
- widgetsets[widgetset].callback(appId);
- } else {
- log("Setting pending startup", appId);
- widgetsets[widgetset].pendingApps.push(appId);
- }
- }
- };
- bootstrapApp(true);
-
- if (getConfig("debug")) {
- // TODO debug state is now global for the entire page, but should somehow only be set for the current application
- window.vaadin.debug = true;
- }
-
- return app;
- },
- clients: {},
- getAppIds: function() {
- var ids = [ ];
- for (var id in apps) {
- if (apps.hasOwnProperty(id)) {
- ids.push(id);
- }
- }
- return ids;
- },
- getApp: function(appId) {
- return apps[appId];
- },
- loadTheme: loadTheme,
- registerWidgetset: function(widgetset, callback) {
- log("Widgetset registered", widgetset);
- var ws = widgetsets[widgetset];
- if (ws && ws.pendingApps) {
- ws.callback = callback;
- for(var i = 0; i < ws.pendingApps.length; i++) {
- var appId = ws.pendingApps[i];
- log("Starting from register widgetset", appId);
- callback(appId);
- }
- ws.pendingApps = null;
- }
- },
- getBrowserDetailsParameters: function(parentElementId, sendUrlsAsParameters) {
- // Screen height and width
- var params = 'v-sh=' + window.screen.height;
- params += '&v-sw=' + window.screen.width;
-
- // Window height and width
- var cw = 0;
- var ch = 0;
- if(typeof(window.innerWidth) == 'number') {
- // Modern browsers
- cw = window.innerWidth;
- ch = window.innerHeight;
- } else {
- // IE 8
- cw = document.documentElement.clientWidth;
- ch = document.documentElement.clientHeight;
- }
- params += '&v-cw=' + cw + '&v-ch=' + ch;
-
-
- var d = new Date();
-
- params += '&v-curdate=' + d.getTime();
-
- var tzo1 = d.getTimezoneOffset(); // current offset
- var dstDiff = 0;
- var rtzo = tzo1;
-
- for (var m=12;m>0;m--) {
- d.setUTCMonth(m);
- var tzo2 = d.getTimezoneOffset();
- if (tzo1 != tzo2) {
- dstDiff = (tzo1 > tzo2 ? tzo1-tzo2 : tzo2-tzo1); // offset w/o DST
- rtzo = (tzo1 > tzo2 ? tzo1 : tzo2); // offset w/o DST
- break;
- }
- }
-
- // Time zone offset
- params += '&v-tzo=' + tzo1;
-
- // DST difference
- params += '&v-dstd=' + dstDiff;
-
- // Raw time zone offset
- params += '&v-rtzo=' + rtzo;
-
- // DST in effect?
- params += '&v-dston=' + (tzo1 != rtzo);
-
- var pe = document.getElementById(parentElementId);
- if (pe) {
- params += '&v-vw=' + pe.offsetWidth;
- params += '&v-vh=' + pe.offsetHeight;
- }
-
- // Location
- if (sendUrlsAsParameters !== false) {
- params += '&v-loc=' + encodeURIComponent(location.href);
- }
-
- // Window name
- if (window.name) {
- params += '&v-wn=' + encodeURIComponent(window.name);
- }
-
- // Detect touch device support
- var supportsTouch = false;
- try {
- document.createEvent("TouchEvent");
- supportsTouch = true;
- } catch (e) {
- // Chrome and IE10 touch detection
- supportsTouch = 'ontouchstart' in window
- || navigator.msMaxTouchPoints;
- }
-
- if (supportsTouch) {
- params += "&v-td=1";
- }
-
- return params;
- }
- };
-
- log('Vaadin bootstrap loaded');
+
+ var loadTheme = function (url, version) {
+ if (!themesLoaded[url]) {
+ log("loadTheme", url, version);
+
+ var href = url + '/styles.css';
+ if (version) {
+ href += '?v=' + version;
+ }
+
+ var stylesheet = document.createElement('link');
+ stylesheet.setAttribute('rel', 'stylesheet');
+ stylesheet.setAttribute('type', 'text/css');
+ stylesheet.setAttribute('href', href);
+ document.getElementsByTagName('head')[0].appendChild(stylesheet);
+ themesLoaded[url] = true;
+ }
+ };
+
+ var isWidgetsetLoaded = function (widgetset) {
+ var className = widgetset.replace(/\./g, "_");
+ return (typeof window[className]) != "undefined";
+ };
+
+ var loadWidgetset = function (url, widgetset, ready) {
+ if (widgetsets[widgetset]) {
+ return;
+ }
+ log("load widgetset", url, widgetset);
+ setTimeout(function () {
+ if (!isWidgetsetLoaded(widgetset)) {
+ if (ready) {
+ alert("Failed to load the widgetset: " + url);
+ } else {
+ if (window.confirm("Failed to load the widgetset. If using CDN for the widgetset, it is possible that compiling it takes up to a few minutes. Would you like to try again?")) {
+ window[widgetset] = undefined;
+ window.location.reload(false);
+ } else {
+ alert("Failed to load the widgetset: " + url);
+ }
+ }
+ }
+ }, 15000);
+
+ var scriptTag = document.createElement('script');
+ scriptTag.setAttribute('type', 'text/javascript');
+ scriptTag.setAttribute('src', url);
+ document.getElementsByTagName('head')[0].appendChild(scriptTag);
+
+ widgetsets[widgetset] = {
+ pendingApps: []
+ };
+ };
+
+ var isInitializedInDom = function (appId) {
+ var appDiv = document.getElementById(appId);
+ if (!appDiv) {
+ return false;
+ }
+ for (var i = 0; i < appDiv.childElementCount; i++) {
+ var className = appDiv.childNodes[i].className;
+ // If the app div contains a child with the class
+ // "v-app-loading" we have only received the HTML
+ // but not yet started the widget set
+ // (UIConnector removes the v-app-loading div).
+ if (className && className.indexOf("v-app-loading") != -1) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ window.vaadin = window.vaadin || {
+ initApplication: function (appId, config) {
+ var testbenchId = appId.replace(/-\d+$/, '');
+
+ if (apps[appId]) {
+ if (window.vaadin && window.vaadin.clients && window.vaadin.clients[testbenchId] && window.vaadin.clients[testbenchId].initializing) {
+ throw "Application " + appId + " is already being initialized";
+ }
+ if (isInitializedInDom(appId)) {
+ throw "Application " + appId + " already initialized";
+ }
+ }
+
+ log("init application", appId, config);
+
+ window.vaadin.clients[testbenchId] = {
+ isActive: function () {
+ return true;
+ },
+ initializing: true
+ };
+
+ var getConfig = function (name) {
+ var value = config[name];
+ return value;
+ };
+
+ var fetchRootConfig = function (callback) {
+ log('Fetching root config');
+ var url = getConfig('browserDetailsUrl');
+ if (!url) {
+ // No special url defined, use the same URL that loaded this page (without the fragment)
+ url = window.location.href.replace(/#.*/, '');
+ }
+ // Timestamp to avoid caching
+ url += ((/\?/).test(url) ? "&" : "?") + "v-" + (new Date()).getTime();
+
+ var params = "v-browserDetails=1";
+ var rootId = getConfig("v-rootId");
+ if (rootId !== undefined) {
+ params += "&v-rootId=" + rootId;
+ }
+
+ // Tell the UI what theme it is configured to use
+ var theme = getConfig('theme');
+ if (theme !== undefined) {
+ params += '&theme=' + encodeURIComponent(theme);
+ }
+
+ params += "&v-appId=" + appId;
+
+ var extraParams = getConfig('extraParams')
+ if (extraParams !== undefined) {
+ params += extraParams;
+ }
+
+ params += '&' + vaadin.getBrowserDetailsParameters(appId, getConfig('sendUrlsAsParameters'));
+
+ var r;
+ try {
+ r = new XMLHttpRequest();
+ } catch (e) {
+ r = new ActiveXObject("MSXML2.XMLHTTP.3.0");
+ }
+ r.open('POST', url, true);
+ r.onreadystatechange = function (aEvt) {
+ if (r.readyState == 4) {
+ // Save responseStatus so as Offline Applications know what happened
+ // when loading root configuration from server, and depending on the
+ // error status display an error message or the offline UI.
+ config.rootResponseStatus = r.status;
+ config.rootResponseText = r.responseText;
+
+ var text = r.responseText;
+ if (r.status == 200) {
+ log("Got root config response", text);
+ var updatedConfig = JSON.parse(text);
+
+ // Copy new properties to the config object
+ for (var property in updatedConfig) {
+ if (updatedConfig.hasOwnProperty(property)) {
+ config[property] = updatedConfig[property];
+ }
+ }
+
+ // Try bootstrapping again, this time without fetching missing info
+ bootstrapApp(false);
+ } else {
+ log('Error', r.statusText, text);
+
+ //Let TB waitForVaadin work again
+ delete window.vaadin.clients[testbenchId];
+
+ // Show the error in the app's div
+ var appDiv = document.getElementById(appId);
+ appDiv.innerHTML = text;
+ appDiv.style['overflow'] = 'auto';
+ }
+
+ // Run the fetchRootConfig callback if present.
+ callback && callback(r);
+ }
+ };
+ // send parameters as POST data
+ r.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ r.send(params);
+
+ log('sending request to ', url);
+ };
+
+ //Export public data
+ var app = {
+ getConfig: getConfig,
+ // Used when the app was started in offline, so as it is possible
+ // to defer root configuration loading until network is available.
+ fetchRootConfig: fetchRootConfig
+ };
+ apps[appId] = app;
+
+ if (!window.name) {
+ window.name = appId + '-' + Math.random();
+ }
+
+ var bootstrapApp = function (mayDefer) {
+ var vaadinDir = getConfig('vaadinDir');
+
+ var versionInfo = getConfig('versionInfo');
+
+ var themeUri = vaadinDir + 'themes/' + getConfig('theme');
+ loadTheme(themeUri, versionInfo && versionInfo['vaadinVersion']);
+
+ var widgetset = getConfig('widgetset');
+ var widgetsetUrl = getConfig('widgetsetUrl');
+ if (!widgetsetUrl) {
+ widgetsetUrl = vaadinDir + 'widgetsets/' + widgetset + "/" + widgetset + ".nocache.js?" + new Date().getTime();
+ }
+ var widgetsetReady = getConfig('widgetsetReady');
+ loadWidgetset(widgetsetUrl, widgetset, widgetsetReady);
+
+ if (getConfig('uidl') === undefined) {
+ if (mayDefer) {
+ fetchRootConfig();
+ } else {
+ throw "May not defer bootstrap any more";
+ }
+ } else {
+ if (widgetsets[widgetset].callback) {
+ log("Starting from bootstrap", appId);
+ widgetsets[widgetset].callback(appId);
+ } else {
+ log("Setting pending startup", appId);
+ widgetsets[widgetset].pendingApps.push(appId);
+ }
+ }
+ };
+ bootstrapApp(true);
+
+ if (getConfig("debug")) {
+ // TODO debug state is now global for the entire page, but should somehow only be set for the current application
+ window.vaadin.debug = true;
+ }
+
+ return app;
+ },
+ clients: {},
+ getAppIds: function () {
+ var ids = [];
+ for (var id in apps) {
+ if (apps.hasOwnProperty(id)) {
+ ids.push(id);
+ }
+ }
+ return ids;
+ },
+ getApp: function (appId) {
+ return apps[appId];
+ },
+ loadTheme: loadTheme,
+ registerWidgetset: function (widgetset, callback) {
+ log("Widgetset registered", widgetset);
+ var ws = widgetsets[widgetset];
+ if (ws && ws.pendingApps) {
+ ws.callback = callback;
+ for (var i = 0; i < ws.pendingApps.length; i++) {
+ var appId = ws.pendingApps[i];
+ log("Starting from register widgetset", appId);
+ callback(appId);
+ }
+ ws.pendingApps = null;
+ }
+ },
+ getBrowserDetailsParameters: function (parentElementId, sendUrlsAsParameters) {
+ // Screen height and width
+ var params = 'v-sh=' + window.screen.height;
+ params += '&v-sw=' + window.screen.width;
+
+ // Window height and width
+ var cw = 0;
+ var ch = 0;
+ if (typeof(window.innerWidth) == 'number') {
+ // Modern browsers
+ cw = window.innerWidth;
+ ch = window.innerHeight;
+ } else {
+ // IE 8
+ cw = document.documentElement.clientWidth;
+ ch = document.documentElement.clientHeight;
+ }
+ params += '&v-cw=' + cw + '&v-ch=' + ch;
+
+
+ var d = new Date();
+
+ params += '&v-curdate=' + d.getTime();
+
+ var tzo1 = d.getTimezoneOffset(); // current offset
+ var dstDiff = 0;
+ var rtzo = tzo1;
+
+ for (var m = 12; m > 0; m--) {
+ d.setUTCMonth(m);
+ var tzo2 = d.getTimezoneOffset();
+ if (tzo1 != tzo2) {
+ dstDiff = (tzo1 > tzo2 ? tzo1 - tzo2 : tzo2 - tzo1); // offset w/o DST
+ rtzo = (tzo1 > tzo2 ? tzo1 : tzo2); // offset w/o DST
+ break;
+ }
+ }
+
+ // Time zone offset
+ params += '&v-tzo=' + tzo1;
+
+ // DST difference
+ params += '&v-dstd=' + dstDiff;
+
+ // Raw time zone offset
+ params += '&v-rtzo=' + rtzo;
+
+ // DST in effect?
+ params += '&v-dston=' + (tzo1 != rtzo);
+
+ // Time zone id (if available)
+ try {
+ params += '&v-tzid=' + encodeURIComponent(Intl.DateTimeFormat().resolvedOptions().timeZone);
+ } catch (err) {
+
+ }
+
+ var pe = document.getElementById(parentElementId);
+ if (pe) {
+ params += '&v-vw=' + pe.offsetWidth;
+ params += '&v-vh=' + pe.offsetHeight;
+ }
+
+ // Location
+ if (sendUrlsAsParameters !== false) {
+ params += '&v-loc=' + encodeURIComponent(location.href);
+ }
+
+ // Window name
+ if (window.name) {
+ params += '&v-wn=' + encodeURIComponent(window.name);
+ }
+
+ // Detect touch device support
+ var supportsTouch = false;
+ try {
+ document.createEvent("TouchEvent");
+ supportsTouch = true;
+ } catch (e) {
+ // Chrome and IE10 touch detection
+ supportsTouch = 'ontouchstart' in window
+ || navigator.msMaxTouchPoints;
+ }
+
+ if (supportsTouch) {
+ params += "&v-td=1";
+ }
+
+ return params;
+ }
+ };
+
+ log('Vaadin bootstrap loaded');
})();