(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 () { }; } else if (typeof console.log === "function") { //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; } 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 getCookie = function (cname) { var b = document.cookie.match('(^|;)\\s*' + cname + '\\s*=\\s*([^;]+)'); return b ? b.pop() : ''; }; 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"); var xsrfToken = getCookie("XSRF-TOKEN"); if (xsrfToken && xsrfToken.length > 0) { r.setRequestHeader("X-XSRF-TOKEN", xsrfToken); } 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); } // This parameter is used in multiplatform-runtime as a key for // storing the MPR UI content in the session if (window.mprUiId) { params += '&v-mui=' + encodeURIComponent(window.mprUiId); } // 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'); })();